Compare commits

...
33 Commits
Author SHA1 Message Date
action b109228443 update 2026-09-15 17:08:00 2026-09-15 17:08:00 +08:00
action bb8ab7ae16 update 2026-09-15 11:00:48 2026-09-15 11:00:48 +08:00
action c323f3b64e update 2026-09-15 01:28:15 2026-09-15 01:28:15 +08:00
action fcd7d31b29 update 2026-09-14 17:28:54 2026-09-14 17:28:54 +08:00
action 87faec277a update 2026-09-14 10:58:48 2026-09-14 10:58:48 +08:00
action d341a782d4 update 2026-09-14 06:01:57 2026-09-14 06:01:57 +08:00
action 205be274cd update 2026-09-14 02:28:52 2026-09-14 02:28:52 +08:00
action 622dd92021 update 2026-09-13 23:28:56 2026-09-13 23:28:56 +08:00
action 141863e6d4 update 2026-09-13 16:42:22 2026-09-13 16:42:22 +08:00
action 2e55e363ac update 2026-09-13 10:43:38 2026-09-13 10:43:38 +08:00
action 86d911baf4 update 2026-09-13 05:53:41 2026-09-13 05:53:41 +08:00
action 65075cb38f update 2026-09-13 02:01:59 2026-09-13 02:01:59 +08:00
action aff13cf0c6 update 2026-09-12 22:51:39 2026-09-12 22:51:39 +08:00
action 7497dff620 update 2026-09-12 16:17:58 2026-09-12 16:17:58 +08:00
action 639738b05d update 2026-09-12 10:45:42 2026-09-12 10:45:42 +08:00
action 419c580042 update 2026-09-12 06:09:18 2026-09-12 06:09:18 +08:00
action 7a00e14e57 update 2026-09-12 02:55:35 2026-09-12 02:55:35 +08:00
action 8daa9142b3 update 2026-09-11 23:43:33 2026-09-11 23:43:33 +08:00
action 42846bd15f update 2026-09-11 16:29:42 2026-09-11 16:29:42 +08:00
action e964d3fab4 update 2026-09-11 10:36:18 2026-09-11 10:36:18 +08:00
action 7a1c066f8e update 2026-09-11 06:12:18 2026-09-11 06:12:18 +08:00
action 7f998ecef4 update 2026-09-11 02:53:12 2026-09-11 02:53:12 +08:00
action eb846719e6 update 2026-09-10 23:42:03 2026-09-10 23:42:03 +08:00
action 11dbae0135 update 2026-09-10 16:35:06 2026-09-10 16:35:06 +08:00
action 52a65b099b update 2026-09-10 10:43:10 2026-09-10 10:43:10 +08:00
action eda104d43a update 2026-09-10 06:10:14 2026-09-10 06:10:14 +08:00
action be31648233 update 2026-09-10 03:01:51 2026-09-10 03:01:51 +08:00
action 542084a121 update 2026-09-09 23:46:59 2026-09-09 23:47:00 +08:00
action fe313e7d23 update 2026-09-09 16:33:06 2026-09-09 16:33:06 +08:00
action 09f0910b78 update 2026-09-09 03:12:17 2026-09-09 03:12:17 +08:00
action 8778465a55 update 2026-09-08 23:51:28 2026-09-08 23:51:28 +08:00
action b81fb0f1c5 update 2026-09-08 16:30:31 2026-09-08 16:30:31 +08:00
action f12aa6537d update 2026-09-08 10:40:39 2026-09-08 10:40:39 +08:00
164 changed files with 6672 additions and 4088 deletions
+2 -2
View File
@@ -5,12 +5,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=brook
PKG_VERSION:=20260101.0
PKG_VERSION:=20270101
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/txthinking/brook/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=8ddba4ed9ae9d10928e169f8121c6791e0e3c2907fa27d6e0055fe434f6e700e
PKG_HASH:=43d8e5476918daa2d35fc63e8b0c94c0c1df8577f1d09103a3ab0f6141f29c0f
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
PKG_LICENSE:=GPL-3.0
+4 -4
View File
@@ -1,8 +1,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=clashoo
PKG_SHORT_SHA:=8f54283
PKG_COMMIT_DATE:=2026.09.07
PKG_SHORT_SHA:=ab405ba
PKG_COMMIT_DATE:=2026.09.14
PKG_VERSION:=$(PKG_COMMIT_DATE)~$(PKG_SHORT_SHA)
PKG_RELEASE:=1
@@ -11,8 +11,8 @@ PKG_SOURCE:=$(PKG_NAME)-alpha-$(PKG_SHORT_SHA).tar.gz
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-alpha-$(PKG_SHORT_SHA)
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-alpha-$(PKG_SHORT_SHA)
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-clashoo/releases/download/mihomo-src
PKG_SOURCE_VERSION:=8f54283e97728859d88f45025bab250504337687
PKG_HASH:=28db96ba63d8f4e79df122c1efe02fed485e1997cf1888e5d045c17908aa34a9
PKG_SOURCE_VERSION:=ab405bad5beeeac8b003bb01f60f134f6df54471
PKG_HASH:=bd7a8035d67755720178a8fd2c49a9d82a4578493aae80ae46d5e6d3141cfaee
PKG_BUILD_VERSION:=alpha-$(PKG_SHORT_SHA)
PKG_LICENSE:=GPL3.0+
+3
View File
@@ -47,6 +47,9 @@ config clashoo 'config'
option fallback_filter_geoip '0'
list fallback_filter_ipcidr '240.0.0.0/4'
option dns_respect_rules '1'
option dns_loopback_compat '0'
list dns_loopback_compat_resolver 'udp://223.5.5.5:53'
list dns_loopback_compat_resolver 'udp://119.29.29.29:53'
option dns_migrated '1'
option block_quic '0'
option singbox_independent_cache '0'
+113 -6
View File
@@ -790,6 +790,11 @@ _dnsmasq_state_digest() {
"$(uci -q get dhcp.@dnsmasq[0].resolvfile 2>/dev/null)"
}
_reload_dnsmasq() {
/etc/init.d/dnsmasq reload >/dev/null 2>&1 && return 0
/etc/init.d/dnsmasq restart >/dev/null 2>&1
}
revert_dns() {
#===========================================================================================================================
local _before _after
@@ -803,7 +808,7 @@ revert_dns() {
return 0
fi
uci commit dhcp
/etc/init.d/dnsmasq restart >/dev/null 2>&1
_reload_dnsmasq >/dev/null 2>&1
#===========================================================================================================================
}
@@ -1040,6 +1045,99 @@ _clashoo_dns_force_fields() {
return 0
}
_dns_nameserver_inherit_mode() {
local has_nameserver marker prefer_h3 respect_false has_proxy force_respect
command -v yq >/dev/null 2>&1 || return 0
[ -s "$CONFIG_YAML" ] || return 0
has_nameserver="$(yq -M e '(.dns.nameserver // null) | (tag != "!!null" and length > 0)' "$CONFIG_YAML" 2>/dev/null)"
[ "$has_nameserver" = "true" ] || return 0
marker=0
if yq -M e -r '(.dns.nameserver // null) | .. | select(tag == "!!str")' "$CONFIG_YAML" 2>/dev/null |
awk '
{
hash = index($0, "#")
if (!hash) next
fragment = substr($0, hash + 1)
split(fragment, parts, "&")
if (parts[1] !~ /=/) { found = 1; exit }
}
END { exit(found ? 0 : 1) }'; then
marker=1
fi
prefer_h3="$(yq -M e '(.dns."prefer-h3" // false) == true' "$CONFIG_YAML" 2>/dev/null)"
respect_false="$(yq -M e '((.dns // {}) | has("respect-rules")) and ((.dns."respect-rules") == false or (.dns."respect-rules") == "false")' "$CONFIG_YAML" 2>/dev/null)"
has_proxy="$(yq -M e '(.dns // {}) | has("proxy-server-nameserver")' "$CONFIG_YAML" 2>/dev/null)"
force_respect=0
case ",${CLASHOO_DNS_FORCE:-}," in
*,respect-rules,*) force_respect=1 ;;
esac
# skip suppresses both the generated proxy role and automatic respect-rules.
if [ "$marker" = "1" ] ||
{ [ "$prefer_h3" = "true" ] && [ "$force_respect" != "1" ]; } ||
{ [ "$respect_false" = "true" ] && [ "$force_respect" != "1" ]; } ||
[ "$(uci -q get clashoo.config.dns_respect_rules 2>/dev/null)" = "0" ]; then
printf '%s' skip
return 0
fi
case ",${CLASHOO_DNS_FORCE:-}," in
*,proxy-server-nameserver,*) return 0 ;;
esac
[ "$has_proxy" = "true" ] && return 0
printf '%s' inherit
}
_adapt_dns_loopback_resolver() {
local filter listen host host_pattern old_port servers replacement tmp
[ "$(uci -q get clashoo.config.dns_loopback_compat 2>/dev/null)" = "1" ] || return 0
command -v yq >/dev/null 2>&1 || return 0
[ -s "$CONFIG_YAML" ] || return 0
filter="${CLASHOO_DNS_LOOPBACK_FILTER:-/usr/share/clashoo/runtime/dns_loopback_adapter.yq}"
[ -r "$filter" ] || return 0
[ "$(yq -M e '(.dns."proxy-server-nameserver" | type)' "$CONFIG_YAML" 2>/dev/null)" = "!!seq" ] || return 0
listen="$(yq -M e -r '.dns.listen // ""' "$CONFIG_YAML" 2>/dev/null)"
old_port="${listen##*:}"
host="${listen%:*}"
printf '%s' "$old_port" | grep -Eq '^[0-9]+$' || return 0
host="$(printf '%s' "$host" | tr 'A-Z' 'a-z')"
case "$host" in
""|0.0.0.0|'[::]'|::)
host_pattern='(localhost|127(\.[0-9]{1,3}){3}|\[?::1\]?)' ;;
localhost)
host_pattern='localhost' ;;
127.*)
printf '%s' "$host" | grep -Eq '^127(\.[0-9]{1,3}){3}$' || return 0
host_pattern="$(printf '%s' "$host" | sed 's/\./\\./g')" ;;
'[::1]'|::1)
host_pattern='\[?::1\]?' ;;
*)
return 0 ;;
esac
servers="$(yq -M e -r '.dns."proxy-server-nameserver"[]' "$CONFIG_YAML" 2>/dev/null)"
printf '%s\n' "$servers" | grep -Eiq "^([A-Za-z][A-Za-z0-9+.-]*://)?(${host_pattern}):${old_port}($|[/#?])" || return 0
replacement="$(uci -q get clashoo.config.dns_loopback_compat_resolver 2>/dev/null)"
[ -n "$replacement" ] || replacement='udp://223.5.5.5:53 udp://119.29.29.29:53'
tmp="$(mktemp "${CONFIG_YAML}.dns-loopback.XXXXXX" 2>/dev/null)" || return 0
export CLASHOO_DNS_OLD_PORT="$old_port"
export CLASHOO_DNS_HOST_PATTERN="$host_pattern"
export CLASHOO_DNS_REPLACEMENT="$replacement"
if yq -M e --from-file "$filter" "$CONFIG_YAML" > "$tmp" 2>/dev/null &&
mv -f "$tmp" "$CONFIG_YAML"; then
log_msg "Adapted stale loopback proxy DNS" "已适配失效的回环节点 DNS"
else
rm -f "$tmp"
log_msg "Loopback proxy DNS adaptation failed; preserved source settings" "回环节点 DNS 适配失败,已保留原配置"
fi
unset CLASHOO_DNS_OLD_PORT CLASHOO_DNS_HOST_PATTERN CLASHOO_DNS_REPLACEMENT
return 0
}
backfill_custom_sniffer_overrides() {
[ "$CLASHOO_KEEP_SNIFFER" = "1" ] || return 0
[ "$(uci -q get clashoo.config.sniffer_streaming 2>/dev/null)" = "1" ] || return 0
@@ -1062,8 +1160,15 @@ ensure_quic_fallback_guard() {
yml_change() {
CLASHOO_DNS_FORCE="$(_clashoo_dns_force_fields)"
CLASHOO_DNS_PRESENT="$(_user_dns_fields "$CLASHOO_DNS_FORCE")"
CLASHOO_DNS_INHERIT_MODE="$(_dns_nameserver_inherit_mode)"
CLASHOO_DNS_INHERIT_NAMESERVER=""
if [ "$CLASHOO_DNS_INHERIT_MODE" = "inherit" ]; then
CLASHOO_DNS_INHERIT_NAMESERVER="$(yq -M e -o=json '(.dns.nameserver // [])' "$CONFIG_YAML" 2>/dev/null)"
fi
export CLASHOO_DNS_FORCE
export CLASHOO_DNS_PRESENT
export CLASHOO_DNS_INHERIT_MODE
export CLASHOO_DNS_INHERIT_NAMESERVER
CLASHOO_KEEP_FAKEIP_FILTER=""
CLASHOO_KEEP_SNIFFER=""
if command -v yq >/dev/null 2>&1 && [ -s "$CONFIG_YAML" ]; then
@@ -1583,8 +1688,7 @@ singbox_dns_change() {
fi
if [ "$(_dnsmasq_state_digest)" != "$_sb_dns_before" ]; then
uci commit dhcp >/dev/null 2>&1
/etc/init.d/dnsmasq stop >/dev/null 2>&1
/etc/init.d/dnsmasq start >/dev/null 2>&1
_reload_dnsmasq >/dev/null 2>&1
else
uci -q revert dhcp >/dev/null 2>&1
fi
@@ -1880,6 +1984,7 @@ _start_prepare_config() {
if command -v yq >/dev/null 2>&1 && grep -q '<<[[:space:]]*:' "$CONFIG_YAML" 2>/dev/null; then
yq e -i 'explode(.)' "$CONFIG_YAML" 2>/dev/null || true
fi
_adapt_dns_loopback_resolver >/dev/null 2>&1
# stage bundled cn.mrs into the core dir; mihomo rejects rule-set paths outside /etc/clashoo
if [ -f /usr/share/clashoo/ruleset/cn.mrs ]; then
if [ ! -f /etc/clashoo/ruleset/cn.mrs ] || [ /usr/share/clashoo/ruleset/cn.mrs -nt /etc/clashoo/ruleset/cn.mrs ]; then
@@ -1993,8 +2098,7 @@ _start_launch_core() {
if [ "$(_dnsmasq_state_digest)" != "$_dnsmasq_digest_before" ]; then
log_msg "Reloading DNS resolver" "重启 DNS 解析"
/etc/init.d/dnsmasq stop >/dev/null 2>&1
/etc/init.d/dnsmasq start >/dev/null 2>&1
_reload_dnsmasq >/dev/null 2>&1
fi
restore >/dev/null 2>&1
@@ -2041,7 +2145,7 @@ boot(){
runtime_state_set "health_status" "starting"
runtime_state_set "health_detail" "boot_starting"
runtime_state_set "updated_at" "$(date +%s)"
# start_service does mihomo -t dry-run + firewall restart + dnsmasq restart
# start_service does mihomo -t dry-run + firewall restart + dnsmasq reload/fallback
# (~20-30s on LXC). Doing this synchronously in boot() blocks rcS S99 stage
# and during that window sshd/uhttpd refuse connections (kex reset). Detach
# to background with a small settle delay so the rcS pipeline finishes and
@@ -2283,6 +2387,9 @@ reload_service()
-X PUT -d '{"path":"'"$CONFIG_YAML"'"}' \
"http://${_lip}:${_dp}/configs?force=true" >/dev/null 2>&1
backend_apply_rules 2>/dev/null
if [ "$(uci -q get clashoo.config.core_only 2>/dev/null)" != "1" ]; then
_reload_dnsmasq >/dev/null 2>&1
fi
runtime_run_health_check_settle >/dev/null 2>&1
fi
}
@@ -939,6 +939,7 @@ set clashoo_china6 {
2402:7240::/32,
2402:72a0::/32,
2402:72c0::/32,
2402:73e0::/32,
2402:7540::/32,
2402:75c0::/32,
2402:7740::/32,
@@ -1246,11 +1247,12 @@ set clashoo_china6 {
2403:6280::/32,
2403:62c0::/32,
2403:6380::/42,
2403:6380:41::/48,
2403:6380:43::/48,
2403:6380:44::/46,
2403:6380:48::/45,
2403:6380:50::/44,
2403:6380:70::/44,
2403:6380:60::/43,
2403:6380:80::/41,
2403:6380:100::/40,
2403:6380:200::/39,
@@ -2036,7 +2038,7 @@ set clashoo_china6 {
2406:840:a18::/45,
2406:840:a20::/44,
2406:840:a30::/48,
2406:840:a32::/47,
2406:840:a33::/48,
2406:840:a34::/46,
2406:840:a38::/45,
2406:840:a40::/42,
@@ -2337,7 +2339,10 @@ set clashoo_china6 {
2406:840:e57f::/48,
2406:840:e580::/41,
2406:840:e610::/44,
2406:840:e620::/43,
2406:840:e622::/47,
2406:840:e624::/46,
2406:840:e628::/45,
2406:840:e630::/44,
2406:840:e640::/43,
2406:840:e660::/46,
2406:840:e664::/47,
@@ -2448,6 +2453,10 @@ set clashoo_china6 {
2406:840:f600::/42,
2406:840:f640::/43,
2406:840:f670::/44,
2406:840:f680::/47,
2406:840:f682::/48,
2406:840:f684::/46,
2406:840:f688::/45,
2406:840:f690::/44,
2406:840:f6a0::/43,
2406:840:f6c0::/42,
@@ -3039,7 +3048,6 @@ set clashoo_china6 {
2408:4008::/29,
2408:4010::/30,
2408:4014::/31,
2408:4016:1::/48,
2408:4016:2::/47,
2408:4016:4::/46,
2408:4016:8::/45,
@@ -920,7 +920,9 @@ set clashoo_china {
59.153.136.0/22,
59.153.152.0/22,
59.153.164.0/22,
59.153.168.0/21,
59.153.168.0/22,
59.153.173.0/24,
59.153.174.0/23,
59.153.176.0/20,
59.153.192.0/22,
59.155.0.0/16,
@@ -951,6 +953,7 @@ set clashoo_china {
61.29.128.0/18,
61.29.192.0/19,
61.29.224.0/20,
61.29.251.0/24,
61.45.128.0/18,
61.45.224.0/20,
61.47.128.0/18,
@@ -2186,6 +2189,7 @@ set clashoo_china {
103.143.132.0/22,
103.143.174.0/23,
103.143.228.0/23,
103.144.41.0/24,
103.144.66.0/23,
103.144.70.0/23,
103.144.72.0/23,
@@ -2341,7 +2345,7 @@ set clashoo_china {
103.176.244.0/23,
103.177.28.0/23,
103.177.44.0/23,
103.177.70.0/23,
103.177.71.0/24,
103.177.162.0/23,
103.178.240.0/23,
103.179.76.0/22,
@@ -2754,6 +2758,7 @@ set clashoo_china {
103.238.24.0/21,
103.238.32.0/21,
103.238.40.0/22,
103.238.46.0/23,
103.238.48.0/21,
103.238.56.0/22,
103.238.88.0/21,
@@ -3224,7 +3229,10 @@ set clashoo_china {
114.112.228.0/24,
114.112.230.0/23,
114.112.234.0/23,
114.112.240.0/20,
114.112.240.0/21,
114.112.248.0/22,
114.112.252.0/23,
114.112.255.0/24,
114.113.0.0/17,
114.113.128.0/21,
114.113.140.0/22,
@@ -3394,7 +3402,9 @@ set clashoo_china {
117.134.128.0/18,
117.134.205.0/24,
117.134.207.0/24,
117.134.208.0/20,
117.134.208.0/23,
117.134.212.0/23,
117.134.216.0/21,
117.134.232.0/21,
117.134.240.0/20,
117.135.0.0/16,
@@ -3746,7 +3756,6 @@ set clashoo_china {
123.49.236.0/24,
123.49.240.0/24,
123.49.242.0/23,
123.49.245.0/24,
123.49.248.0/21,
123.50.160.0/19,
123.52.0.0/14,
@@ -3981,7 +3990,14 @@ set clashoo_china {
140.179.0.0/16,
140.205.0.0/16,
140.206.0.0/15,
140.210.0.0/16,
140.210.0.0/20,
140.210.16.0/21,
140.210.24.0/22,
140.210.28.0/23,
140.210.30.0/24,
140.210.32.0/19,
140.210.64.0/18,
140.210.128.0/17,
140.224.0.0/16,
140.237.0.0/16,
140.240.0.0/16,
@@ -4214,6 +4230,7 @@ set clashoo_china {
163.47.4.0/22,
163.52.28.0/23,
163.52.76.0/23,
163.52.108.0/23,
163.53.0.0/20,
163.53.36.0/22,
163.53.40.0/21,
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,17 @@
. as $document |
strenv(CLASHOO_DNS_OLD_PORT) as $old_port |
strenv(CLASHOO_DNS_HOST_PATTERN) as $host_pattern |
(strenv(CLASHOO_DNS_REPLACEMENT) | split(" ") | map(select(length > 0))) as $replacement |
("(?i)^(?:[a-z][a-z0-9+.-]*://)?(?:" + $host_pattern + "):" + $old_port + "(?:$|[/#?])") as $old_loopback |
(
$document |
select((.dns."proxy-server-nameserver" | type) == "!!seq") |
([.dns."proxy-server-nameserver"[] | select(type == "!!str" and test($old_loopback))] | length) as $match_count |
select($match_count > 0) |
.dns."proxy-server-nameserver" =
([
.dns."proxy-server-nameserver"[] |
select((type == "!!str" and test($old_loopback)) | not) |
select(. as $server | ($replacement | contains([$server])) | not)
] + $replacement)
) // $document
@@ -12,10 +12,13 @@ trap 'rm -f "$CUSTOM_RULE_FILE" "$RULE" "$CLASH" "$CLASH_CONFIG"' EXIT
[ -f "$CONFIG_YAML" ] || exit 0
. /usr/share/clashoo/runtime/primary_group.sh
# __PROXY__ placeholder -> primary proxy group (cached by RPC);
# fallback to GLOBAL (built-in mihomo group)
PRIMARY_GROUP="$(uci -q get clashoo.config.primary_proxy_group)"
CACHED_PRIMARY_GROUP="$(uci -q get clashoo.config.primary_proxy_group)"
PRIMARY_GROUP="$(clashoo_resolve_primary_group "$CONFIG_YAML" "$CACHED_PRIMARY_GROUP")"
[ -n "$PRIMARY_GROUP" ] || PRIMARY_GROUP="GLOBAL"
append=$(uci get clashoo.config.append_rules 2>/dev/null)
@@ -119,6 +119,14 @@ for (let f in split(s(getenv('CLASHOO_DNS_PRESENT'), ''), ','))
let dns_force = {};
for (let f in split(s(getenv('CLASHOO_DNS_FORCE'), ''), ','))
if (length(trim(f))) dns_force[trim(f)] = true;
let dns_inherit_mode = s(getenv('CLASHOO_DNS_INHERIT_MODE'), '');
let inherited_nameserver = json(s(getenv('CLASHOO_DNS_INHERIT_NAMESERVER'), '[]'));
if (type(inherited_nameserver) != 'array') {
if (inherited_nameserver != null && inherited_nameserver != '')
inherited_nameserver = [inherited_nameserver];
else
inherited_nameserver = [];
}
let dns_role_fields = ['nameserver', 'proxy-server-nameserver', 'direct-nameserver',
'default-nameserver', 'nameserver-policy', 'respect-rules'];
let had_user_dns_roles = false;
@@ -158,8 +166,15 @@ uci.foreach('clashoo', 'dnsservers', function(sec) {
if (!dns_roles[role]) dns_roles[role] = [];
push(dns_roles[role], srv);
});
for (let role in keys(dns_roles))
for (let role in keys(dns_roles)) {
if (role == 'proxy-server-nameserver' && (dns_inherit_mode == 'inherit' || dns_inherit_mode == 'skip') &&
!dns_present[role] && !dns_force[role]) continue;
if (!dns_present[role] || dns_force[role]) cfg['dns'][role] = dns_roles[role];
}
if (dns_inherit_mode == 'inherit' && length(inherited_nameserver) &&
!dns_present['proxy-server-nameserver'] && !dns_force['proxy-server-nameserver'])
cfg['dns']['proxy-server-nameserver'] = inherited_nameserver;
let bootstrap = a('default_nameserver');
if (bootstrap == null) bootstrap = a('defaul_nameserver');
@@ -182,8 +197,10 @@ if (length(keys(policies)) && (!dns_present['nameserver-policy'] || dns_force['n
cfg['dns']['nameserver-policy'] = policies;
let respect_rules = a('dns_respect_rules') == null ? true : ab('dns_respect_rules');
if (respect_rules && length(cfg['dns']['proxy-server-nameserver'] || [])
&& (!dns_present['respect-rules'] || dns_force['respect-rules']) && !dns_present['prefer-h3'])
if (dns_force['respect-rules'])
cfg['dns']['respect-rules'] = respect_rules;
else if (respect_rules && length(cfg['dns']['proxy-server-nameserver'] || [])
&& !dns_present['respect-rules'] && !dns_present['prefer-h3'])
cfg['dns']['respect-rules'] = true;
/* profile */
+49
View File
@@ -0,0 +1,49 @@
#!/bin/sh
clashoo_resolve_primary_group() {
local config_yaml="$1"
local cached_group="$2"
local group_rows
local resolved_group
if [ "$cached_group" = "GLOBAL" ]; then
printf '%s\n' "GLOBAL"
return 0
fi
if [ ! -f "$config_yaml" ] || ! command -v yq >/dev/null 2>&1; then
printf '%s\n' "GLOBAL"
return 0
fi
group_rows="$(yq -M e -r '(."proxy-groups" // [])[] | [.name, .type] | @tsv' "$config_yaml" 2>/dev/null)" || {
printf '%s\n' "GLOBAL"
return 0
}
if [ -n "$cached_group" ] && printf '%s\n' "$group_rows" |
awk -F '\t' -v wanted="$cached_group" '$1 == wanted { found = 1; exit } END { exit !found }'
then
printf '%s\n' "$cached_group"
return 0
fi
resolved_group="$(printf '%s\n' "$group_rows" | awk -F '\t' '
tolower($2) == "select" &&
(index($1, "节点选择") || index($1, "代理") || index($1, "🚀") || tolower($1) ~ /proxy/) {
print $1
exit
}
')"
if [ -z "$resolved_group" ]; then
resolved_group="$(printf '%s\n' "$group_rows" | awk -F '\t' '
tolower($2) == "select" {
print $1
exit
}
')"
fi
printf '%s\n' "${resolved_group:-GLOBAL}"
}
@@ -301,9 +301,10 @@ package_version_from_url() {
case "$file" in
clashoo_*_"$ARCH".ipk)
v="${file#clashoo_}"
printf '%s\n' "${v%_${ARCH}.ipk}"
v="${v%_${ARCH}.ipk}"
printf '%s\n' "$v" | sed 's/^\([0-9][0-9][0-9][0-9]\.[0-9][0-9]*\.[0-9][0-9]*\)\./\1~/'
;;
clashoo_*.ipk) printf '%s\n' "$file" | sed -n 's/^clashoo_\(.*\)_[^_][^_]*\.ipk$/\1/p' ;;
clashoo_*.ipk) printf '%s\n' "$file" | sed -n 's/^clashoo_\(.*\)_[^_][^_]*\.ipk$/\1/p' | sed 's/^\([0-9][0-9][0-9][0-9]\.[0-9][0-9]*\.[0-9][0-9]*\)\./\1~/' ;;
luci-app-clashoo_*.ipk) printf '%s\n' "$file" | sed -n 's/^luci-app-clashoo_\(.*\)_all\.ipk$/\1/p' ;;
luci-i18n-clashoo-zh-cn_*.ipk) printf '%s\n' "$file" | sed -n 's/^luci-i18n-clashoo-zh-cn_\(.*\)_all\.ipk$/\1/p' ;;
clashoo-*.apk)
+3 -3
View File
@@ -5,13 +5,13 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=dae
PKG_VERSION:=2026.09.06
PKG_VERSION:=2026.09.12
PKG_RELEASE:=1
PKG_SOURCE:=dae-src-2026.09.06-bb80d29edd0f.tar.gz
PKG_SOURCE:=dae-src-2026.09.12-187058462a1f.tar.gz
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/dae-src
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_HASH:=bb80d29edd0faecaa4184c32ba43d601271d60cfc7fe920c3fa5ad4be91e2dbc
PKG_HASH:=187058462a1fd9eeb9885f4971ccf4bc81358533e71730d8509bd7968624c1c7
PKG_LICENSE:=AGPL-3.0-only
PKG_LICENSE_FILE:=LICENSE
+3 -3
View File
@@ -5,13 +5,13 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=daed
PKG_VERSION:=2026.09.06
PKG_VERSION:=2026.09.12
PKG_RELEASE:=1
PKG_SOURCE:=daed-src-2026.09.06-00979296e45e.tar.gz
PKG_SOURCE:=daed-src-2026.09.12-a0181f729855.tar.gz
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_HASH:=00979296e45ef7f4aab704ca131e9f686194b61b2500b7483c48722366278d36
PKG_HASH:=a0181f7298552497ed193e683a54b1df77f2d5441524d61989f3e3e3cf6eac51
PKG_LICENSE:=AGPL-3.0-only MIT
PKG_LICENSE_FILES:=LICENSE wing/LICENSE
+83 -4
View File
@@ -1,8 +1,65 @@
#!/bin/sh
# daed-cleanup.sh — reaper for stale daed kernel state.
#
# Removes:
# 1. Processes inside the `daens` netns (SIGTERM, then SIGKILL after 1s)
# 2. The `daens` network namespace itself (ip netns del, with
# umount+rm as fallback for zombie nsfs mounts)
# 3. The `dae0` veth pair in the host netns
#
# daed itself owns TC clsact detach. This opkg-side helper only
# reaps stale daens/dae0 state and never removes /sys/fs/bpf/daed.
# The pin directory is created during normal startup, so it is not
# a reliable leak indicator and must not block service lifecycle.
#
# The function stays sourceable by both init.d/daed and daed-guard.
daed_process_probe() {
if command -v pgrep >/dev/null 2>&1; then
pgrep -f '^/usr/bin/daed([[:space:]]|$)' >/dev/null 2>&1
case "$?" in
0) return 0 ;;
1) return 1 ;;
esac
fi
if command -v pidof >/dev/null 2>&1; then
pidof daed >/dev/null 2>&1
case "$?" in
0) return 0 ;;
1) return 1 ;;
esac
fi
return 2
}
daed_cleanup_runtime() {
local pid
local pid rc=0 probe_rc
# If daed userspace is currently running, do not touch the
# netns, the veth, or the eBPF dataplane. They are in active
# use; removing them would make every connection through dae
# hang.
daed_process_probe
probe_rc=$?
case "$probe_rc" in
0)
if [ "${DAED_GUARD_CLEANUP:-start}" = "start" ]; then
logger -t daed-init "cleanup: pre-start skipped because /usr/bin/daed is still running; refusing a second instance"
return 1
fi
logger -t daed-init "cleanup: post-exit skipped because another /usr/bin/daed instance is still running"
return 0
;;
1) ;;
*)
logger -t daed-init "cleanup: cannot determine whether daed is running; refusing to remove netns/veth"
return 1
;;
esac
# 1. Kill processes inside daens.
for pid in $(ip netns pids daens 2>/dev/null); do
kill "$pid" 2>/dev/null
done
@@ -14,14 +71,36 @@ daed_cleanup_runtime() {
done
fi
# 2. Remove the daens netns. ip netns del can fail if a
# process still references it via /proc/<pid>/ns/net or
# because the umount has already happened. Try the
# umount/rm fallback.
if ! ip netns del daens 2>/dev/null; then
umount -l /run/netns/daens 2>/dev/null
rm -f /run/netns/daens
if [ -e /run/netns/daens ] && ! rm -f /run/netns/daens 2>/dev/null; then
logger -t daed-init "cleanup: failed to remove /run/netns/daens (resource busy); a reboot may be required"
rc=1
fi
fi
ip link del dae0 2>/dev/null || true
# 3. Remove the dae0 veth pair.
if ip link show dae0 >/dev/null 2>&1; then
if ! ip link del dae0 2>/dev/null; then
logger -t daed-init "cleanup: failed to remove dae0 veth pair"
rc=1
fi
fi
# 4. The pin root is normal persistent state. Never remove it or
# make its existence change the cleanup result.
if [ -e /sys/fs/bpf/daed ]; then
logger -t daed-init "cleanup: /sys/fs/bpf/daed exists; leaving normal pin root unchanged"
fi
# Final verification covers only netns and veth state.
[ ! -e /run/netns/daens ] || return 1
! ip netns list 2>/dev/null | awk '$1 == "daens" { found=1 } END { exit !found }' || return 1
! ip netns list 2>/dev/null | grep -Eq '^daens([[:space:]]|$)' || return 1
! ip link show dae0 >/dev/null 2>&1 || return 1
return $rc
}
+118 -16
View File
@@ -1,29 +1,131 @@
#!/bin/sh
# Keep daed as a child so signals can be forwarded and stale netns/veth
# state can be cleaned before start and after exit. daed owns TC detach;
# /sys/fs/bpf/daed is normal persistent state and is never removed here.
. /usr/share/daed/cleanup.sh
# Pre-start cleanup refuses to remove runtime state while another
# daed instance is active, and fails closed if that probe is broken.
DAED_GUARD_CLEANUP=start
if ! daed_cleanup_runtime; then
echo "daed: stale network state could not be removed" >&2
echo "daed: stale /usr/bin/daed or netns state could not be verified or removed; refusing to start. Check process and netns state." >&2
logger -t daed-init "pre-start cleanup failed: refusing to start daed"
exit 1
fi
# Keep daed as a child so a panic or unexpected exit is followed by an
# immediate teardown of all kernel/runtime state before procd can respawn us.
# Keep daed as a child so post-exit cleanup runs before procd can
# respawn it.
child_pid=
cleanup_child() {
[ -n "$child_pid" ] && kill "$child_pid" 2>/dev/null
pending_signal=
shutdown_signal=
shutdown_elapsed=0
forced_kill=0
child_term_timeout=20
forward_signal() {
local sig="$1"
if [ -z "$child_pid" ]; then
pending_signal="$sig"
logger -t daed-init "signal $sig received before daed child started; launch cancelled"
return 0
fi
case "$sig" in
TERM|INT|QUIT)
if [ -z "$shutdown_signal" ]; then
shutdown_signal="$sig"
shutdown_elapsed=0
fi
;;
esac
kill -"$sig" "$child_pid" 2>/dev/null
}
trap cleanup_child TERM INT
/usr/bin/daed "$@" &
child_pid=$!
wait "$child_pid"
status=$?
child_pid=
trap - TERM INT
exit_with_signal() {
local sig="$1"
trap - TERM INT HUP QUIT
kill -"$sig" "$$" 2>/dev/null
exit 1
}
if ! daed_cleanup_runtime; then
echo "daed: runtime cleanup after exit failed" >&2
status=1
child_is_running() {
local state
kill -0 "$child_pid" 2>/dev/null || return 1
state=$(awk '{ print $3 }' "/proc/$child_pid/stat" 2>/dev/null)
[ "$state" != "Z" ]
}
trap 'forward_signal TERM' TERM
trap 'forward_signal INT' INT
trap 'forward_signal HUP' HUP
trap 'forward_signal QUIT' QUIT
start_child() {
local sig
# Keep this check inside the function as well as at the call site:
# it closes the ordinary pre-start window, while the pending-signal
# path below handles a signal arriving during the background fork.
[ -z "$pending_signal" ] || return 125
/usr/bin/daed "$@" &
child_pid=$!
if [ -n "$pending_signal" ]; then
sig="$pending_signal"
pending_signal=
forward_signal "$sig"
fi
}
if [ -n "$pending_signal" ]; then
logger -t daed-init "refusing to start daed after pending signal $pending_signal"
exit_with_signal "$pending_signal"
fi
exit "$status"
# Best-effort OOM preference; failure must not block startup.
if ! echo -16 > /proc/self/oom_score_adj 2>/dev/null; then
logger -t daed-init "warn: failed to set /proc/self/oom_score_adj; continuing without OOM preference"
fi
if ! start_child "$@"; then
logger -t daed-init "refusing to start daed after pending signal $pending_signal"
if [ -n "$pending_signal" ]; then
exit_with_signal "$pending_signal"
fi
exit 1
fi
status=0
reaped=0
while [ "$reaped" -eq 0 ]; do
if [ -n "$shutdown_signal" ] && child_is_running; then
if [ "$shutdown_elapsed" -ge "$child_term_timeout" ]; then
if [ "$forced_kill" -eq 0 ]; then
logger -t daed-init "daed did not exit within ${child_term_timeout}s after $shutdown_signal; sending KILL"
kill -KILL "$child_pid" 2>/dev/null
forced_kill=1
fi
else
sleep 1
shutdown_elapsed=$((shutdown_elapsed + 1))
continue
fi
sleep 1
continue
fi
wait "$child_pid" 2>/dev/null
status=$?
if ! child_is_running; then
reaped=1
fi
done
child_pid=
trap - TERM INT HUP QUIT
# Post-exit cleanup runs after the child is reaped.
DAED_GUARD_CLEANUP=post-exit
cleanup_status=0
daed_cleanup_runtime || cleanup_status=$?
if [ "$cleanup_status" -ne 0 ]; then
echo "daed: runtime cleanup after exit failed" >&2
logger -t daed-init "post-exit cleanup failed; check ip netns / ip link show"
fi
if [ "$status" -ne 0 ]; then
exit "$status"
fi
exit "$cleanup_status"
+37 -13
View File
@@ -1,5 +1,7 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2023 Tianling Shen <cnsztl@immortalwrt.org>
# daed-guard handles bounded child shutdown and post-exit netns/veth cleanup.
# Keep the log file and a pre-stop state snapshot for diagnostics.
USE_PROCD=1
START=99
@@ -10,18 +12,28 @@ LOG="/var/log/daed/daed.log"
. /usr/share/daed/cleanup.sh
log() {
logger -t daed-init "$@"
}
start_service() {
log "start: begin"
config_load "$CONF"
local enabled
config_get_bool enabled "config" "enabled" "0"
[ "$enabled" -eq "1" ] || return 1
if [ "$enabled" -ne "1" ]; then
log "start: config disabled, exit"
return 1
fi
local listen_addr log_maxbackups log_maxsize
config_get listen_addr "config" "listen_addr" "0.0.0.0:2023"
config_get log_maxbackups "config" "log_maxbackups" "1"
config_get log_maxsize "config" "log_maxsize" "5"
log "start: listen=$listen_addr log_maxbackups=$log_maxbackups log_maxsize=$log_maxsize"
procd_open_instance "$CONF"
procd_set_param env DAE_LOCATION_ASSET="/usr/share/v2ray" TZ="$(uci -q get system.@system[0].zonename)"
procd_set_param command "$PROG" run
@@ -33,25 +45,37 @@ start_service() {
procd_set_param limits core="unlimited"
procd_set_param limits nofile="1000000 1000000"
# Avoid an endless crash/respawn loop which can repeatedly reattach dae's
# data-plane hooks and make the router management plane unreachable.
procd_set_param respawn 3600 5 5
# daed-guard escalates its child after 20 seconds. Leave time for
# reap and post-exit cleanup before procd kills the wrapper.
procd_set_param term_timeout 30
# procd_set_param respawn: arguments are (threshold, timeout, retry).
# threshold = runtime that resets the short-lived exit counter
# timeout = seconds to wait between retries
# retry = maximum short-lived exits before procd gives up
# Reset the counter after one hour of stable runtime; otherwise retry
# after 5 seconds and stop after 10 failures.
procd_set_param respawn 3600 5 10
# daed-guard sets oom_score_adj before forking; procd has no
# oom_adj/oom_score_adj parameter.
# procd_set_param stdout 1
procd_set_param stderr 1
procd_close_instance
log "start: procd_open_instance done"
}
stop_service() {
rm -f "$LOG"
daed_cleanup_runtime
}
restart() {
stop
sleep 1
daed_cleanup_runtime
start
log "stop: begin"
# Cleanup runs in daed-guard after its child exits. Record only a
# pre-stop snapshot here; pin entries do not prove TC attachment.
local pinned="" ns_left=""
if [ -d /sys/fs/bpf/daed ]; then
pinned=$(ls /sys/fs/bpf/daed 2>/dev/null | tr '\n' ' ')
fi
if ip netns list 2>/dev/null | grep -q '^daens'; then
ns_left="daens"
fi
log "stop: pre-stop state — bpf_pin_entries=[${pinned:-none}] netns_left=[${ns_left:-none}]"
}
service_triggers() {
+11 -9
View File
@@ -1,8 +1,6 @@
# SPDX-License-Identifier: GPL-3.0-only
#
# Copyright (C) 2021-2023 sirpdboy <herboy2008@gmail.com>
#
# This is free software, licensed under the Apache License, Version 2.0 .
# Copyright (C) 2021-2026 sirpdboy <herboy2008@gmail.com>
#
include $(TOPDIR)/rules.mk
@@ -10,12 +8,12 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=ddns-go
PKG_VERSION:=6.17.7
PKG_RELEASE:=1
PKG_VERSION:=6.17.7
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/jeessy2/ddns-go/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=f7001004e092d9641aad5a94158e0b4cae4a53a7f5c7d96d5c6af3d246c56fcc
PKG_LICENSE:=MIT
PKG_HASH:=f7001004e092d9641aad5a94158e0b4cae4a53a7f5c7d96d5c6af3d246c56fcc
PKG_LICENSE_FILES:=LICENSE
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
@@ -44,14 +42,18 @@ define Package/ddns-go/description
support Alidns Dnspod Cloudflare Hicloud Callback Baiducloud porkbun GoDaddy Google Domains.
endef
define Package/ddns-go/conffiles
/etc/config/ddns-go
/etc/ddns-go/ddns-go-config.yaml
endef
define Package/ddns-go/install
$(call GoPackage/Package/Install/Bin,$(1))
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) $(CURDIR)/file/ddns-go.init $(1)/etc/init.d/ddns-go
$(INSTALL_DIR) $(1)/etc/uci-defaults
$(INSTALL_BIN) $(CURDIR)/file/luci-ddns-go.uci-default $(1)/etc/uci-defaults/luci-ddns-go
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_BIN) $(CURDIR)/files/ddns-go.init $(1)/etc/init.d/ddns-go
$(INSTALL_CONF) $(CURDIR)/files/ddns-go.conf $(1)/etc/config/ddns-go
endef
$(eval $(call GoBinPackage,ddns-go))
-46
View File
@@ -1,46 +0,0 @@
#!/bin/sh /etc/rc.common
#
# Copyright (C) 2021-2023 sirpdboy <herboy2008@gmail.com> https://github.com/sirpdboy/luci-app-ddns-go
#
# This file is part of ddns-go .
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
START=99
USE_PROCD=1
PROG=/usr/bin/ddns-go
CONFDIR=/etc/ddns-go
CONF=$CONFDIR/ddns-go-config.yaml
get_config() {
config_get_bool enabled $1 enabled 1
config_get_bool logger $1 logger 1
config_get port $1 port 9876
config_get time $1 time 300
}
init_yaml(){
[ -d $CONFDIR ] || mkdir -p $CONFDIR 2>/dev/null
cat /usr/share/ddns-go/ddns-go-default.yaml > $CONF
}
start_service() {
config_load ddns-go
config_foreach get_config basic
[ x$enabled == x1 ] || return 1
[ -s ${CONF} ] || init_yaml
logger -t ddns-go -p warn "ddns-go is start."
echo "ddns-go is start."
procd_open_instance
procd_set_param command $PROG -l :$port -f $time -c "$CONF"
[ "x$logger" == x1 ] && procd_set_param stderr 1
procd_set_param respawn
procd_close_instance
}
service_triggers() {
procd_add_reload_trigger "ddns-go"
}
-7
View File
@@ -1,7 +0,0 @@
#!/bin/sh
[ -s "/etc/ddns-go/localtime" ] && mv -f /etc/ddns-go/localtime /etc/localtime
/etc/init.d/ddns-go enable
/etc/init.d/ddns-go start
rm -f /tmp/luci*
exit 0
+9
View File
@@ -0,0 +1,9 @@
config basic 'config'
option enabled '0'
option logger '1'
option port '9876'
option time '300'
option ctimes '5'
option skipverify '0'
option delay '0'
option dns '223.5.5.5'
+85
View File
@@ -0,0 +1,85 @@
#!/bin/sh /etc/rc.common
#
# Copyright (C) 2021-2026 sirpdboy <herboy2008@gmail.com>
#
# This file is part of ddns-go .
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
START=99
USE_PROCD=1
NAME=ddns-go
PROG=/usr/bin/ddns-go
CONFDIR=/etc/ddns-go
CONF=$CONFDIR/ddns-go-config.yaml
init_yaml() {
[ -d "$CONFDIR" ] || mkdir -p "$CONFDIR"
chown -R ddns-go:ddns-go "$CONFDIR"
chmod 755 "$CONFDIR"
[ -f "$CONF" ] && chmod 644 "$CONF"
}
build_args() {
local cfg="$1"
local args="-c $CONF"
config_get port "$cfg" port '9876'
args="$args -l :$port"
config_get time "$cfg" time '300'
[ -n "$time" ] && args="$args -f $time"
config_get ctimes "$cfg" ctimes '5'
[ -n "$ctimes" ] && args="$args -cacheTimes $ctimes"
config_get dns "$cfg" dns '223.5.5.5'
[ -n "$dns" ] && args="$args -dns $dns"
config_get_bool noweb "$cfg" noweb 0
[ "$noweb" -eq 1 ] && args="$args -noweb"
config_get_bool skipverify "$cfg" skipverify 0
[ "$skipverify" -eq 1 ] && args="$args -skipVerify"
echo "$args"
}
start_instance() {
local cfg="$1"
local logger
config_get_bool enabled "$cfg" enabled 0
[ "$enabled" -eq 0 ] && return 0
config_get delay "$cfg" delay 0
if [ "$delay" -gt 0 ]; then
local uptime=$(awk -F. '{print $1}' /proc/uptime)
[ "$uptime" -lt 120 ] && sleep "$delay"
fi
init_yaml
local args=$(build_args "$cfg")
procd_open_instance
procd_set_param command $PROG $args
config_get_bool logger "$cfg" logger 1
procd_set_param stdout "$logger"
procd_set_param stderr "$logger"
procd_set_param user ddns-go
procd_set_param respawn
procd_close_instance
}
start_service() {
config_load "$NAME"
config_foreach start_instance 'basic'
}
service_triggers() {
procd_add_reload_trigger "$NAME"
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 319 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 640 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 550 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 547 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 659 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 650 KiB

+3 -3
View File
@@ -9,9 +9,9 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://gn.googlesource.com/gn.git
PKG_SOURCE_DATE:=2026-09-03
PKG_SOURCE_VERSION:=4f6a76b64b8279e98004f541f8e136307efe5e01
PKG_MIRROR_HASH:=933dc8c8d745923437c9e730ab7c2a23a53562144513a4f634ad10381c23be44
PKG_SOURCE_DATE:=2026-09-11
PKG_SOURCE_VERSION:=cfcd774b98f3433e18b722f9a7ff06119825b8eb
PKG_MIRROR_HASH:=d8bea9ac89f9e87f36874601588f0d8d32221bf1ad8488f2fd9775abcddf4860
PKG_LICENSE:=BSD 3-Clause
PKG_LICENSE_FILES:=LICENSE
+2 -2
View File
@@ -3,7 +3,7 @@
#ifndef OUT_LAST_COMMIT_POSITION_H_
#define OUT_LAST_COMMIT_POSITION_H_
#define LAST_COMMIT_POSITION_NUM 2553
#define LAST_COMMIT_POSITION "2553 (4f6a76b64b82)"
#define LAST_COMMIT_POSITION_NUM 2563
#define LAST_COMMIT_POSITION "2563 (cfcd774b98f3)"
#endif // OUT_LAST_COMMIT_POSITION_H_
+1 -1
View File
@@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-clashoo
PKG_VERSION:=1.30.0
PKG_RELEASE:=12
PKG_RELEASE:=15
PKG_MAINTAINER:=kenzok8
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
@@ -143,7 +143,7 @@ const callOverview = rpc.declare({ object: 'luci.clashoo', method: 'ove
const callSmartFlushCache = rpc.declare({ object: 'luci.clashoo', method: 'smart_flush_cache', expect: {} });
const callListSingboxProfiles = rpc.declare({ object: 'luci.clashoo', method: 'list_singbox_profiles', expect: {} });
const callGetSingboxProfile = rpc.declare({ object: 'luci.clashoo', method: 'get_singbox_profile', params: ['name'], expect: {} });
const callSaveSingboxProfile = rpc.declare({ object: 'luci.clashoo', method: 'save_singbox_profile', params: ['name', 'content'], expect: {} });
const callSaveSingboxProfileChunk = rpc.declare({ object: 'luci.clashoo', method: 'save_singbox_profile_chunk', params: ['name', 'content', 'index', 'total'], expect: {} });
const callSetSingboxProfile = rpc.declare({ object: 'luci.clashoo', method: 'set_singbox_profile', params: ['name'], expect: {} });
const callDeleteSingboxProfile = rpc.declare({ object: 'luci.clashoo', method: 'delete_singbox_profile', params: ['name'], expect: {} });
const callCreateSingboxConfig = rpc.declare({ object: 'luci.clashoo', method: 'create_singbox_config', params: ['sub_url', 'name'], expect: {} });
@@ -235,7 +235,23 @@ return baseclass.extend({
listSingboxProfiles: function () { return L.resolveDefault(callListSingboxProfiles(), { profiles: [], active: '' }); },
getSingboxProfile: function (name) { return L.resolveDefault(callGetSingboxProfile(name), {}); },
saveSingboxProfile: function (name, content){ return L.resolveDefault(callSaveSingboxProfile(name, content), {}); },
saveSingboxProfile: function (name, content) {
var chunkSize = 24576;
var total = Math.max(1, Math.ceil((content || '').length / chunkSize));
var index = 0;
function sendNext() {
var chunk = (content || '').slice(index * chunkSize, (index + 1) * chunkSize);
return L.resolveDefault(callSaveSingboxProfileChunk(name, chunk, String(index), String(total)), {}).then(function (r) {
if (!r || !r.success)
return { success: false, error: (r && r.error) || 'upload_failed', message: (r && (r.message || r.error)) || _('Upload failed') };
index++;
return index < total ? sendNext() : r;
});
}
return sendNext();
},
setSingboxProfile: function (name) { return L.resolveDefault(callSetSingboxProfile(name), {}); },
deleteSingboxProfile: function (name) { return L.resolveDefault(callDeleteSingboxProfile(name), {}); },
createSingboxConfig: function (url, name) { return L.resolveDefault(callCreateSingboxConfig(url, name), {}); },
@@ -1357,6 +1357,14 @@ return view.extend({
o = s.option(form.Flag, 'dns_respect_rules', _("DNS Respect Rules"));
o.default = '1';
o.description = _("DNS queries follow the routing rules, so overseas domains resolve through the proxy instead of a polluted result. Requires proxy-server-nameserver.");
o = s.option(form.Flag, 'dns_loopback_compat', _("OpenClash/Nikki Loopback DNS Compatibility"));
o.default = '0';
o.description = _("Optional compatibility for normal managed mode. At runtime, replace only proxy node resolvers that point back to the imported configuration's own DNS listener. Core Only mode stays verbatim; the source configuration and unrelated local DNS services remain unchanged.");
o.depends('enable_dns', '1');
o = s.option(form.DynamicList, 'dns_loopback_compat_resolver', _("Loopback DNS Replacement"));
o.placeholder = 'udp://223.5.5.5:53';
o.description = _("Used only when the compatibility switch detects a stale self-reference. When empty, udp://223.5.5.5:53 and udp://119.29.29.29:53 are used. Plain IP DNS is recommended to avoid bootstrap loops.");
o.depends('dns_loopback_compat', '1');
o = s.option(form.Value, 'dns_ecs', _("ECS Client Subnet"));
o.placeholder = _("Recommended blank");
o.description = _("mihomo writes the ecs parameter to DNS URLs; sing-box writes dns.client_subnet. Leave empty to skip.");
+12
View File
@@ -902,9 +902,18 @@ msgstr ""
msgid "OpenClash is enabled; disable it first to avoid rule conflicts"
msgstr ""
msgid "OpenClash/Nikki Loopback DNS Compatibility"
msgstr ""
msgid "Operation failed: "
msgstr ""
msgid "Loopback DNS Replacement"
msgstr ""
msgid "Optional compatibility for normal managed mode. At runtime, replace only proxy node resolvers that point back to the imported configuration's own DNS listener. Core Only mode stays verbatim; the source configuration and unrelated local DNS services remain unchanged."
msgstr ""
msgid "Other Configuration Files (upload / overwrite output)"
msgstr ""
@@ -1502,6 +1511,9 @@ msgstr ""
msgid "User-Agent (UA)"
msgstr ""
msgid "Used only when the compatibility switch detects a stale self-reference. When empty, udp://223.5.5.5:53 and udp://119.29.29.29:53 are used. Plain IP DNS is recommended to avoid bootstrap loops."
msgstr ""
msgid "Username"
msgstr ""
+12
View File
@@ -909,6 +909,18 @@ msgstr "TCP 或 UDP 任意一个选择 TUN 时,该栈设置才会生效。"
msgid "OpenClash is enabled; disable it first to avoid rule conflicts"
msgstr "OpenClash 已启用,请先停用以避免规则冲突"
msgid "OpenClash/Nikki Loopback DNS Compatibility"
msgstr "OpenClash/Nikki 回环 DNS 兼容"
msgid "Loopback DNS Replacement"
msgstr "回环 DNS 替换地址"
msgid "Optional compatibility for normal managed mode. At runtime, replace only proxy node resolvers that point back to the imported configuration's own DNS listener. Core Only mode stays verbatim; the source configuration and unrelated local DNS services remain unchanged."
msgstr "用于普通接管模式的可选兼容功能。运行时仅替换指回导入配置自身 DNS 监听的节点解析器;仅内核模式保持原样,源配置和其他本地 DNS 服务不受影响。"
msgid "Used only when the compatibility switch detects a stale self-reference. When empty, udp://223.5.5.5:53 and udp://119.29.29.29:53 are used. Plain IP DNS is recommended to avoid bootstrap loops."
msgstr "仅在兼容开关检测到失效自引用时使用。留空则使用 udp://223.5.5.5:53 和 udp://119.29.29.29:53。建议使用纯 IP DNS,避免启动解析死循环。"
msgid "Operation failed: "
msgstr "操作失败: "
@@ -1135,7 +1135,7 @@ return '_merged_' + s + '__' + t + '.yaml';
}
function template_search_dirs() {
return [TEMPLATE_USER_DIR, TEMPLATE_DIR, '/usr/share/clashoo/config/template'];
return [TEMPLATE_DIR, '/usr/share/clashoo/config/template'];
}
function find_template_path(name) {
@@ -3318,6 +3318,8 @@ call: function(req) {
let tpl_src = trim(req.args?.template_source || '');
let sub_url = trim(req.args?.sub_url || '');
let output_name = ensure_yaml_name(req.args?.output_name || '');
if (!output_name) output_name = 'tpl-rewrite.yaml';
let uniq_name = replace(output_name, /\.(yaml|yml)$/, '');
let set_active = (req.args?.set_active || '') == '1';
if (!tpl_src)
@@ -3361,20 +3363,16 @@ return { success: false, error: 'missing_yq', message: '缺少 yq,无法注入
if (!access(exploded, "r")) {
system("cp -f " + shell_quote(tmp_tpl) + " " + shell_quote(exploded));
}
/* 替换 proxy-providers.urlrule-providers 指向规则集保持原样 */
let inject_cmd = "URL=" + shell_quote(sub_url)
+ " yq e '.[\"proxy-providers\"][].url = env(URL)' "
+ shell_quote(exploded) + " > " + shell_quote(tmp_out);
/* 替换 proxy-providers.url 并把 provider 名改成唯一名(同步 proxy-groups 的 use*/
let inject_cmd = "URL=" + shell_quote(sub_url) + " NAME=" + shell_quote(uniq_name)
+ " yq e '.[\"proxy-providers\"][].url = env(URL) | (.[\"proxy-providers\"] | keys | map({\"key\": ., \"value\": strenv(NAME) + \"-\" + .}) | from_entries) as $rename | .[\"proxy-providers\"] |= with_entries(.key = $rename[.key]) | (.[\"proxy-groups\"][] | select(has(\"use\")) | .use) |= map($rename[.] // .)' "
+ shell_quote(exploded) + " > " + shell_quote(tmp_out);
if (system(inject_cmd) != 0 || !access(tmp_out, "r")) {
system("rm -f " + shell_quote(exploded));
return { success: false, error: "inject_failed", message: "订阅 URL 注入失败" };
}
system("rm -f " + shell_quote(exploded));
if (!output_name) {
output_name = 'tpl-rewrite.yaml';
}
let out_path = out_dir + '/' + output_name;
if (system('mv -f ' + shell_quote(tmp_out) + ' ' + shell_quote(out_path)) != 0)
return { success: false, error: 'write_failed', message: '写入结果文件失败' };
@@ -3382,16 +3380,16 @@ return { success: false, error: 'write_failed', message: '写入结果文件失
/* 删除 respect-rules(模板可能设了但没提供 proxy-server-nameserver */
system("sed -i '/respect-rules/d' " + shell_quote(out_path) + " 2>/dev/null");
system('rm -f ' + shell_quote(tmp_tpl));
/* 替换 直连→DIRECT(兼容不同模板的命名差异) */
system("sed -i 's/直连/DIRECT/g' " + shell_quote(out_path) + " 2>/dev/null");
system("yq e '(.proxy-groups[].proxies) |= sub(\"直连\", \"DIRECT\")' " + shell_quote(out_path) + " -i 2>/dev/null");
/* 只把独立引用项"直连"转成 DIRECT,组名里的"直连"不动 */
system("sed -i 's/^\\( *- *\\)直连$/\\1DIRECT/' " + shell_quote(out_path) + " 2>/dev/null");
system("yq e '(.proxy-groups[] | select(has(\"proxies\")) | .proxies) |= map(sub(\"^直连$\", \"DIRECT\"))' " + shell_quote(out_path) + " -i 2>/dev/null");
/* 用 mixin.uc + yq merge 叠加 UCI 覆盖(端口、routing-mark、dns 等) */
let mixin_script = "/usr/share/clashoo/runtime/mixin.uc";
if (access(mixin_script, "r") && system("command -v yq >/dev/null 2>&1") == 0) {
let mixin_yaml = "/tmp/_clashoo_mixin.yaml";
let merged_out = out_path + ".merged";
system("ucode " + shell_quote(mixin_script) + " 2>/dev/null | yq -M -p json -o yaml > " + shell_quote(mixin_yaml) + " 2>/dev/null");
system("yq -M eval-all '. as $item ireduce ({}; . * $item)' " + shell_quote(out_path) + " " + shell_quote(mixin_yaml) + " > " + shell_quote(merged_out) + " 2>/dev/null");
system("yq -M eval-all '(. as $item ireduce ({}; . * $item)) as $m | [\"port\",\"socks-port\",\"redir-port\",\"tproxy-port\",\"mixed-port\",\"allow-lan\",\"bind-address\",\"mode\",\"log-level\",\"ipv6\",\"interface-name\",\"routing-mark\",\"external-controller\",\"external-ui\",\"secret\",\"tun\",\"listeners\",\"tunnels\",\"dns\",\"hosts\",\"profile\",\"geodata-mode\",\"geodata-loader\",\"geox-url\",\"proxy-providers\",\"proxies\",\"proxy-groups\",\"rule-providers\",\"rules\",\"sub-rules\",\"ntp\",\"authentication\",\"sniffer\",\"experimental\"] as $order | ($order | to_entries | map({\"key\": .value, \"value\": .key}) | from_entries) as $rank | $m | to_entries | sort_by(.key as $k | $rank[$k] // 999) | from_entries' " + shell_quote(out_path) + " " + shell_quote(mixin_yaml) + " > " + shell_quote(merged_out) + " 2>/dev/null");
if (access(merged_out, "r")) {
system("mv -f " + shell_quote(merged_out) + " " + shell_quote(out_path));
}
@@ -3812,6 +3810,50 @@ return { success: true, name };
}
},
save_singbox_profile_chunk: {
args: { name: 'name', content: 'content', index: 'index', total: 'total' },
call: function(req) {
let name = req.args?.name || '';
let content = req.args?.content;
let idx = int(req.args?.index || 0);
let total = int(req.args?.total || 0);
if (!name || index(name, '/') >= 0 || index(name, '..') >= 0)
return { success: false, error: 'invalid name' };
if (!match(name, /\.json$/)) name = name + '.json';
if (type(content) != 'string') return { success: false, error: 'invalid content' };
if (total < 1 || idx < 0 || idx >= total) return { success: false, error: 'invalid chunk' };
if (total == 1 && !content) return { success: false, error: 'empty content' };
let dir = '/usr/share/clashoo/config/singbox';
system('mkdir -p ' + shell_quote(dir) + ' >/dev/null 2>&1');
let tmp = '/tmp/clashoo_sb_upload_' + name;
if (idx == 0) {
if (writefile(tmp, content) === null)
return { success: false, error: 'write_failed', message: '写入文件失败' };
} else {
let fp = open(tmp, 'a');
if (!fp) return { success: false, error: 'write_failed', message: '打开文件失败' };
let ok = fp.write(content);
fp.close();
if (ok === null) return { success: false, error: 'write_failed', message: '写入文件失败' };
}
if (idx + 1 < total)
return { success: true, name, index: idx, total, complete: false };
let binary = find_binary();
if (binary && match(binary, /sing-box/)) {
if (system(shell_quote(binary) + ' check -c ' + shell_quote(tmp) + ' >/dev/null 2>&1') != 0) {
system('rm -f ' + shell_quote(tmp));
return { success: false, error: 'invalid_config', message: 'sing-box 配置验证失败,请检查 JSON 格式' };
}
}
if (system('cp -f ' + shell_quote(tmp) + ' ' + shell_quote(dir + '/' + name) + ' >/dev/null 2>&1') != 0) {
system('rm -f ' + shell_quote(tmp));
return { success: false, error: 'write_failed', message: '写入文件失败' };
}
system('rm -f ' + shell_quote(tmp));
return { success: true, name, index: idx, total, complete: true };
}
},
set_singbox_profile: {
args: { name: 'name' },
call: function(req) {
+1 -2
View File
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for fastnet
LUCI_DEPENDS:=+fastnet
LUCI_PKGARCH:=all
PKG_VERSION:=0.7.2-r1
PKG_VERSION:=0.7.7-r1
# PKG_RELEASE MUST be empty for luci.mk
PKG_RELEASE:=
LUCI_MINIFY_CSS:=0
@@ -17,4 +17,3 @@ LUCI_MINIFY_JS:=0
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
+2 -2
View File
@@ -7,9 +7,9 @@
include $(TOPDIR)/rules.mk
LUCI_TITLE:=KaiPlus
PKG_VERSION:=1.0.1
PKG_VERSION:=1.0.9
PKG_RELEASE:=1
LUCI_DEPENDS:=+kaiplus +luci-compat
LUCI_DEPENDS:=+kaiplus +luci-compat +luci-lib-linkeaseauth
LUCI_MINIFY_CSS:=0
LUCI_MINIFY_JS:=0
@@ -62,4 +62,10 @@ base_path.default = "/apps/kaiplus/"
base_path.rmempty = false
base_path.readonly = true
local release_channel = s:option(ListValue, "release_channel", translate("Release channel"))
release_channel:value("stable", translate("Stable"))
release_channel:value("canary", translate("Canary"))
release_channel.default = "stable"
release_channel.rmempty = false
return m
+1 -1
View File
@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-mosdns
PKG_VERSION:=1.7.13
PKG_VERSION:=1.7.14
PKG_RELEASE:=1
LUCI_TITLE:=LuCI Support for mosdns
@@ -160,8 +160,18 @@ const injectStyles = () => {
'.btn-allow:hover { background: #059669 !important; color: #fff !important; }',
'.btn-block { background: rgba(239, 68, 68, 0.12) !important; color: #dc2626 !important; border: 1px solid rgba(239, 68, 68, 0.3) !important; }',
'.btn-block:hover { background: #dc2626 !important; color: #fff !important; }',
'.mosdns-pagination-bar { display: flex !important; justify-content: space-between !important; align-items: center !important; margin-top: 0.5rem !important; padding: 0.75rem 0.5rem 0.25rem 0.5rem !important; border-top: 1px solid var(--border-color-medium, rgba(125,125,125,0.15)) !important; flex-wrap: wrap !important; gap: 0.5rem !important; box-sizing: border-box !important; width: 100% !important; }',
'.mosdns-pagination-bar::after, .mosdns-pagination-bar::before { display: none !important; content: none !important; }',
'.mosdns-pagination-info { display: inline-flex !important; align-items: center !important; font-size: 0.85rem !important; opacity: 0.85 !important; }',
'.mosdns-pagination-btns { display: flex !important; gap: 0.5rem !important; margin-left: auto !important; }',
'.mosdns-page-input { height: 24px !important; line-height: 22px !important; text-align: center !important; font-size: 0.82rem !important; font-weight: 600 !important; padding: 0 4px !important; margin: 0 4px !important; border: 1px solid rgba(125,125,125,0.3) !important; border-radius: 4px !important; background: var(--cbi-section-bg, #fff) !important; color: inherit !important; display: inline-block !important; vertical-align: middle !important; box-sizing: border-box !important; transition: all 0.2s ease !important; }',
'.mosdns-page-input:focus { border-color: #3b82f6 !important; box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.25) !important; outline: none !important; }',
'.mosdns-page-input::-webkit-outer-spin-button, .mosdns-page-input::-webkit-inner-spin-button { -webkit-appearance: none !important; margin: 0 !important; }',
'.mosdns-page-input[type=number] { -moz-appearance: textfield !important; }',
'@keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; } }',
'@media (prefers-color-scheme: dark) {',
' .mosdns-pagination-bar { border-top-color: rgba(255,255,255,0.08) !important; }',
' .mosdns-page-input { background: rgba(255,255,255,0.06) !important; border-color: rgba(255,255,255,0.18) !important; color: #fff !important; }',
' .mosdns-stat-card, .mosdns-rank-panel, .mosdns-modal-meta-item, .mosdns-answer-row { background: rgba(255,255,255,0.03); border-color: rgba(255,255,255,0.08); box-shadow: none; }',
' .mosdns-sparkline-tooltip { background: #1e242b; border-color: rgba(255,255,255,0.15); box-shadow: 0 4px 12px rgba(0,0,0,0.5); }',
' .mosdns-log-row:hover { background: rgba(255,255,255,0.04); }',
@@ -864,7 +874,7 @@ const renderLogsTable = logsData => {
E('td', { class: 'td col-time', style: 'font-size: 0.82rem; opacity: 0.7; white-space: nowrap;' }, formatTimestamp(item.timestamp)),
E('td', {
class: 'td col-client mosdns-mono',
style: 'font-size: 0.82rem; max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;',
style: 'font-size: 0.82rem; max-width: 190px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;',
title: clientInfo.title
}, clientInfo.display),
E('td', { class: 'td col-domain', style: 'max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;', title: item.domain || '-' }, [
@@ -887,47 +897,99 @@ const renderLogsTable = logsData => {
]));
}
return E('div', {}, [
E('table', { class: 'table cbi-section-table mosdns-table', style: 'margin-top: 0.25rem;' }, [
E('tr', { class: 'tr table-titles' }, [
E('th', { class: 'th col-time', style: 'width: 85px;' }, _('Time')),
E('th', { class: 'th col-client', style: 'width: 125px;' }, _('Client IP')),
E('th', { class: 'th col-domain' }, _('Domain & Record')),
E('th', { class: 'th col-status', style: 'width: 90px;' }, _('Status')),
E('th', { class: 'th col-answers' }, _('Answers')),
E('th', { class: 'th col-latency', style: 'width: 80px; text-align: right;' }, _('Elapsed'))
]),
...rows
]),
const inputWidth = Math.max(46, (String(totalPages).length * 8 + 18)) + 'px';
const pageInput = E('input', {
type: 'number',
class: 'cbi-input-text mosdns-page-input',
min: 1,
max: totalPages,
value: pageIdx + 1,
style: `width: ${inputWidth};`,
title: _('Enter page number and press Enter to jump')
});
E('div', { style: 'display: flex; justify-content: space-between; align-items: center; margin-top: 0.75rem;' }, [
E('span', { style: 'font-size: 0.85rem; opacity: 0.7;' }, _('Page %d / %d (%d entries)').format(pageIdx + 1, totalPages, total)),
E('div', { style: 'display: flex; gap: 0.5rem;' }, [
E('button', {
class: 'btn cbi-button cbi-button-action',
disabled: pageIdx === 0 ? 'disabled' : null,
click: () => {
if (pageIdx > 0) {
pageIdx--;
updateLiveStatusBadge();
refreshLogs();
const doJump = () => {
const val = parseInt(pageInput.value, 10);
if (isNaN(val)) {
pageInput.value = pageIdx + 1;
return;
}
const targetPage = Math.max(1, Math.min(totalPages, val));
pageInput.value = targetPage;
if (targetPage - 1 !== pageIdx) {
pageIdx = targetPage - 1;
updateLiveStatusBadge();
refreshLogs();
}
};
pageInput.addEventListener('keydown', ev => {
if (ev.key === 'Enter') {
ev.preventDefault();
pageInput.blur();
}
});
pageInput.addEventListener('focus', () => {
pageInput.select();
});
pageInput.addEventListener('blur', () => {
doJump();
});
const pageParts = _('Page %d / %d (%d entries)').format(999999, totalPages, total).split('999999');
const paginationLabel = E('div', {
class: 'mosdns-pagination-info'
}, [
pageParts[0] || '',
pageInput,
pageParts[1] || ''
]);
return E('div', {}, [
E('table', { class: 'table cbi-section-table mosdns-table', style: 'margin-top: 0.25rem; margin-bottom: 0;' }, [
E('tr', { class: 'tr table-titles' }, [
E('th', { class: 'th col-time', style: 'width: 85px;' }, _('Time')),
E('th', { class: 'th col-client', style: 'width: 125px;' }, _('Client IP')),
E('th', { class: 'th col-domain' }, _('Domain & Record')),
E('th', { class: 'th col-status', style: 'width: 90px;' }, _('Status')),
E('th', { class: 'th col-answers' }, _('Answers')),
E('th', { class: 'th col-latency', style: 'width: 95px; text-align: right;' }, _('Elapsed'))
]),
...rows
]),
E('div', {
class: 'mosdns-pagination-bar'
}, [
paginationLabel,
E('div', { class: 'mosdns-pagination-btns' }, [
E('button', {
class: 'btn cbi-button cbi-button-action',
disabled: pageIdx === 0 ? 'disabled' : null,
click: () => {
if (pageIdx > 0) {
pageIdx--;
updateLiveStatusBadge();
refreshLogs();
}
}
}
}, _('Previous')),
E('button', {
class: 'btn cbi-button cbi-button-action',
disabled: (pageIdx + 1) >= totalPages ? 'disabled' : null,
click: () => {
if ((pageIdx + 1) < totalPages) {
pageIdx++;
updateLiveStatusBadge();
refreshLogs();
}, _('Previous')),
E('button', {
class: 'btn cbi-button cbi-button-action',
disabled: (pageIdx + 1) >= totalPages ? 'disabled' : null,
click: () => {
if ((pageIdx + 1) < totalPages) {
pageIdx++;
updateLiveStatusBadge();
refreshLogs();
}
}
}
}, _('Next'))
}, _('Next'))
])
])
])
]);
]);
};
const pollScheduler = async () => {
+77 -73
View File
@@ -17,19 +17,19 @@ msgstr ""
msgid "API Options"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:718
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:728
msgid "Add domain to the blocklist to block DNS resolution:"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:717
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:727
msgid "Add domain to the whitelist to permit DNS resolution:"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:710
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:720
msgid "Add to Blocklist"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:710
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:720
msgid "Add to Whitelist"
msgstr ""
@@ -90,7 +90,7 @@ msgstr ""
msgid "Aliyun Public DNS (DNS over QUIC)"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1026
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1088
msgid "All Queries"
msgstr ""
@@ -99,8 +99,8 @@ msgstr ""
msgid "Another update is already in progress."
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:805
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:897
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:815
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:957
msgid "Answers"
msgstr ""
@@ -108,7 +108,7 @@ msgstr ""
msgid "Apple domains optimization"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1053
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1115
msgid ""
"Are you sure you want to clear all real-time query logs and top rankings?"
msgstr ""
@@ -127,11 +127,11 @@ msgid ""
"rules through scheduled tasks."
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:478
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:488
msgid "Average Processing Time"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:538
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:548
msgid "Avg Processing"
msgstr ""
@@ -155,8 +155,8 @@ msgid ""
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:31
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:633
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:635
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:643
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:645
msgid "Block Lists"
msgstr ""
@@ -164,11 +164,11 @@ msgstr ""
msgid "Block PTR"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1027
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1089
msgid "Blocked Only"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:456
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:466
msgid "Blocked by Filters"
msgstr ""
@@ -190,16 +190,16 @@ msgstr ""
msgid "Cache Prefetching"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1028
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1090
msgid "Cached Only"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:467
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:477
msgid "Cached Queries"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:730
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1058
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:740
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1120
msgid "Cancel"
msgstr ""
@@ -229,7 +229,7 @@ msgstr ""
msgid "Cisco Public DNS (208.67.222.222)"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1078
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1140
msgid "Clear"
msgstr ""
@@ -237,22 +237,22 @@ msgstr ""
msgid "Clear logs"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1049
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1052
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1111
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1114
msgid "Clear query logs"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:861
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:878
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:871
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:888
msgid "Click to view full details"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:787
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:894
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:797
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:954
msgid "Client IP"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:832
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:842
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:35
msgid "Close"
msgstr ""
@@ -292,7 +292,7 @@ msgstr ""
msgid "Configuration Editor"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:734
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:744
msgid "Confirm"
msgstr ""
@@ -330,7 +330,7 @@ msgstr ""
msgid "DNS Forward"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:444
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:454
msgid "DNS Queries Total"
msgstr ""
@@ -374,20 +374,20 @@ msgstr ""
msgid "DoH/TCP/DoT Connection Multiplexing idle timeout (default 30 seconds)"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:895
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:955
msgid "Domain & Record"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:627
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:655
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:637
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:665
msgid "Domain name cannot be empty."
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:720
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:730
msgid "Domain:"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:898
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:958
msgid "Elapsed"
msgstr ""
@@ -425,6 +425,10 @@ msgstr ""
msgid "Enabled"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:908
msgid "Enter page number and press Enter to jump"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:505
msgid "Enter the GeoIP.dat category to be exported, Allow add multiple tags"
msgstr ""
@@ -478,7 +482,7 @@ msgstr ""
msgid "Failed to clean logs."
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1072
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1134
msgid "Failed to clear query logs:"
msgstr ""
@@ -486,15 +490,15 @@ msgstr ""
msgid "Failed to start update."
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:970
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1032
msgid "Failed to update query logs:"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:699
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:709
msgid "Failed to update rule file: %s"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1044
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1106
msgid "First Page / Resume"
msgstr ""
@@ -595,10 +599,10 @@ msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:410
msgid ""
"IPv4 CIDR: <a href=\"https://www.cloudflare.com/ips-v4\" "
"target=\"_blank\">https://www.cloudflare.com/ips-v4</a> <br /> IPv6 CIDR: <a "
"href=\"https://www.cloudflare.com/ips-v6\" target=\"_blank\">https://www."
"cloudflare.com/ips-v6</a>"
"IPv4 CIDR: <a href=\"https://www.cloudflare.com/ips-v4\" target=\"_blank"
"\">https://www.cloudflare.com/ips-v4</a> <br /> IPv6 CIDR: <a href=\"https://"
"www.cloudflare.com/ips-v6\" target=\"_blank\">https://www.cloudflare.com/ips-"
"v6</a>"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:190
@@ -621,7 +625,7 @@ msgstr ""
msgid "Info"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:828
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:838
msgid "Intercept"
msgstr ""
@@ -631,7 +635,7 @@ msgid ""
"seconds)."
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:479
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:489
msgid "Latency"
msgstr ""
@@ -716,12 +720,12 @@ msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:33
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:106
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/logs.js:70
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1094
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1156
#: luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json:3
msgid "MosDNS"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:510
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:520
msgid ""
"MosDNS API is unreachable. Please ensure MosDNS is running and stats_api "
"plugin is enabled."
@@ -739,15 +743,15 @@ msgstr ""
msgid "Netflix, Disney+, Hulu and streaming media rules list will use this DNS"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:927
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:989
msgid "Next"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:768
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:778
msgid "No DNS answer records returned."
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:560
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:570
msgid "No data available"
msgstr ""
@@ -755,19 +759,19 @@ msgstr ""
msgid "No log data."
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:886
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:896
msgid "No query log entries found."
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:904
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:941
msgid "Page %d / %d (%d entries)"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:438
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:448
msgid "Per-query speed"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:821
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:831
msgid "Permit"
msgstr ""
@@ -802,7 +806,7 @@ msgstr ""
msgid "Prevent DNS Leaks"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:916
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:978
msgid "Previous"
msgstr ""
@@ -821,7 +825,7 @@ msgstr ""
msgid "Quad9 Public DNS (9.9.9.9)"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:811
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:821
msgid "Query Log Details"
msgstr ""
@@ -831,7 +835,7 @@ msgid ""
"consume more memory)"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1067
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1129
msgid "Query logs cleared successfully."
msgstr ""
@@ -839,7 +843,7 @@ msgstr ""
msgid "RUNNING"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1098
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1160
msgid "Real-time Query Logs"
msgstr ""
@@ -869,7 +873,7 @@ msgstr ""
msgid "Ring Buffer Capacity"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:799
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:809
msgid "Rule Hit"
msgstr ""
@@ -889,7 +893,7 @@ msgstr ""
msgid "Save the cache locally and reload the cache dump on the next startup"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1015
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1077
msgid "Search domain or client IP..."
msgstr ""
@@ -901,7 +905,7 @@ msgstr ""
msgid "Starting update..."
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1094
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1156
#: luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json:38
msgid "Statistics"
msgstr ""
@@ -910,7 +914,7 @@ msgstr ""
msgid "Stats Dump"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:896
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:956
msgid "Status"
msgstr ""
@@ -930,7 +934,7 @@ msgstr ""
msgid "TCP/DoT Connection Multiplexing"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:723
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:733
msgid "Target file: %s (supports domain matching rules)"
msgstr ""
@@ -966,20 +970,20 @@ msgid ""
"content in yaml format."
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:791
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:893
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:801
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:953
msgid "Time"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:599
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:609
msgid "Top Blocked Domains"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:606
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:616
msgid "Top Clients"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:592
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:602
msgid "Top Queried Domains"
msgstr ""
@@ -1029,7 +1033,7 @@ msgstr ""
msgid "Updating Database..."
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:795
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:805
msgid "Upstream"
msgstr ""
@@ -1051,8 +1055,8 @@ msgid ""
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:23
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:633
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:635
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:643
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:645
msgid "White Lists"
msgstr ""
@@ -1070,15 +1074,15 @@ msgstr ""
msgid "https://gh-proxy.com"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:427
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:540
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:437
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:550
msgid "● Live"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:223
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:233
msgid "● Live Auto-refresh"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:227
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:237
msgid "❚❚ Paused (Page %d)"
msgstr ""
+81 -86
View File
@@ -27,19 +27,19 @@ msgstr "API 监听端口"
msgid "API Options"
msgstr "API 选项"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:718
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:728
msgid "Add domain to the blocklist to block DNS resolution:"
msgstr "将域名添加到黑名单以拦截 DNS 解析:"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:717
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:727
msgid "Add domain to the whitelist to permit DNS resolution:"
msgstr "将域名添加到白名单以放行 DNS 解析:"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:710
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:720
msgid "Add to Blocklist"
msgstr "添加到黑名单"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:710
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:720
msgid "Add to Whitelist"
msgstr "添加到白名单"
@@ -105,7 +105,7 @@ msgstr "阿里云公共 DNSDNS over HTTPS"
msgid "Aliyun Public DNS (DNS over QUIC)"
msgstr "阿里云公共 DNSDNS over QUIC"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1026
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1088
msgid "All Queries"
msgstr "所有查询"
@@ -114,8 +114,8 @@ msgstr "所有查询"
msgid "Another update is already in progress."
msgstr "另一个更新正在进行中。"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:805
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:897
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:815
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:957
msgid "Answers"
msgstr "应答结果"
@@ -123,7 +123,7 @@ msgstr "应答结果"
msgid "Apple domains optimization"
msgstr "Apple 域名解析优化"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1053
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1115
msgid ""
"Are you sure you want to clear all real-time query logs and top rankings?"
msgstr "确定要清空所有实时查询日志和排行数据吗?"
@@ -142,11 +142,11 @@ msgid ""
"rules through scheduled tasks."
msgstr "通过定时任务自动更新 GeoIP 和 GeoSite 数据库以及广告过滤规则。"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:478
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:488
msgid "Average Processing Time"
msgstr "平均延迟"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:538
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:548
msgid "Avg Processing"
msgstr "平均处理"
@@ -172,8 +172,8 @@ msgstr ""
"解析。"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:31
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:633
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:635
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:643
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:645
msgid "Block Lists"
msgstr "黑名单"
@@ -181,11 +181,11 @@ msgstr "黑名单"
msgid "Block PTR"
msgstr "PTR 黑名单"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1027
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1089
msgid "Blocked Only"
msgstr "仅拦截"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:456
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:466
msgid "Blocked by Filters"
msgstr "拦截总数"
@@ -207,16 +207,16 @@ msgstr "自动保存缓存"
msgid "Cache Prefetching"
msgstr "缓存预取"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1028
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1090
msgid "Cached Only"
msgstr "仅缓存"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:467
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:477
msgid "Cached Queries"
msgstr "缓存命中"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:730
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1058
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:740
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1120
msgid "Cancel"
msgstr "取消"
@@ -246,7 +246,7 @@ msgstr "思科公共 DNS208.67.220.220"
msgid "Cisco Public DNS (208.67.222.222)"
msgstr "思科公共 DNS208.67.222.222"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1078
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1140
msgid "Clear"
msgstr "清空"
@@ -254,22 +254,22 @@ msgstr "清空"
msgid "Clear logs"
msgstr "清空日志"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1049
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1052
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1111
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1114
msgid "Clear query logs"
msgstr "清除查询日志"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:861
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:878
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:871
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:888
msgid "Click to view full details"
msgstr "点击查看完整详情"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:787
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:894
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:797
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:954
msgid "Client IP"
msgstr "客户端 IP"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:832
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:842
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:35
msgid "Close"
msgstr "关闭"
@@ -309,7 +309,7 @@ msgstr "配置文件"
msgid "Configuration Editor"
msgstr "配置编辑器"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:734
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:744
msgid "Confirm"
msgstr "确认"
@@ -348,7 +348,7 @@ msgstr "DNS 缓存大小"
msgid "DNS Forward"
msgstr "DNS 转发"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:444
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:454
msgid "DNS Queries Total"
msgstr "DNS 查询总数"
@@ -393,20 +393,20 @@ msgstr ""
msgid "DoH/TCP/DoT Connection Multiplexing idle timeout (default 30 seconds)"
msgstr "DoH/TCP/DoT 连接复用空闲保持时间(默认 30 秒)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:895
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:955
msgid "Domain & Record"
msgstr "域名与类型"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:627
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:655
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:637
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:665
msgid "Domain name cannot be empty."
msgstr "域名不能为空"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:720
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:730
msgid "Domain:"
msgstr "域名:"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:898
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:958
msgid "Elapsed"
msgstr "耗时"
@@ -444,6 +444,10 @@ msgstr "启用此选项 fallback 策略会强制转发到远程 DNS"
msgid "Enabled"
msgstr "启用"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:908
msgid "Enter page number and press Enter to jump"
msgstr "输入页码并按回车跳转"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:505
msgid "Enter the GeoIP.dat category to be exported, Allow add multiple tags"
msgstr "输入需要导出的 GeoIP.dat 类别条目,允许添加多个标签"
@@ -497,7 +501,7 @@ msgstr "导出目录:/var/mosdns"
msgid "Failed to clean logs."
msgstr "清理日志失败:%s"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1072
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1134
msgid "Failed to clear query logs:"
msgstr "清除查询日志失败:"
@@ -505,15 +509,15 @@ msgstr "清除查询日志失败:"
msgid "Failed to start update."
msgstr "启动更新失败"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:970
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1032
msgid "Failed to update query logs:"
msgstr "更新查询日志失败:"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:699
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:709
msgid "Failed to update rule file: %s"
msgstr "更新规则文件失败: %s"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1044
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1106
msgid "First Page / Resume"
msgstr "重置并恢复"
@@ -614,15 +618,15 @@ msgstr "IP 地址"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:410
msgid ""
"IPv4 CIDR: <a href=\"https://www.cloudflare.com/ips-v4\" "
"target=\"_blank\">https://www.cloudflare.com/ips-v4</a> <br /> IPv6 CIDR: <a "
"href=\"https://www.cloudflare.com/ips-v6\" target=\"_blank\">https://www."
"cloudflare.com/ips-v6</a>"
"IPv4 CIDR: <a href=\"https://www.cloudflare.com/ips-v4\" target=\"_blank"
"\">https://www.cloudflare.com/ips-v4</a> <br /> IPv6 CIDR: <a href=\"https://"
"www.cloudflare.com/ips-v6\" target=\"_blank\">https://www.cloudflare.com/ips-"
"v6</a>"
msgstr ""
"IPv4 CIDR: <a href=\"https://www.cloudflare.com/ips-v4\" "
"target=\"_blank\">https://www.cloudflare.com/ips-v4</a> <br /> IPv6 CIDR: <a "
"href=\"https://www.cloudflare.com/ips-v6\" target=\"_blank\">https://www."
"cloudflare.com/ips-v6</a>"
"IPv4 CIDR: <a href=\"https://www.cloudflare.com/ips-v4\" target=\"_blank"
"\">https://www.cloudflare.com/ips-v4</a> <br /> IPv6 CIDR: <a href=\"https://"
"www.cloudflare.com/ips-v6\" target=\"_blank\">https://www.cloudflare.com/ips-"
"v6</a>"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:190
msgid ""
@@ -644,7 +648,7 @@ msgstr "空闲超时"
msgid "Info"
msgstr "信息"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:828
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:838
msgid "Intercept"
msgstr "拦截"
@@ -654,7 +658,7 @@ msgid ""
"seconds)."
msgstr "后台线程扫描缓存以进行预取的时间间隔(单位:秒)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:479
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:489
msgid "Latency"
msgstr "延迟"
@@ -742,12 +746,12 @@ msgstr "修改 DNS 应答结果的最小 TTL 值 (秒),0 表示不修改"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:33
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:106
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/logs.js:70
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1094
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1156
#: luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json:3
msgid "MosDNS"
msgstr "MosDNS"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:510
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:520
msgid ""
"MosDNS API is unreachable. Please ensure MosDNS is running and stats_api "
"plugin is enabled."
@@ -767,15 +771,15 @@ msgstr "未运行"
msgid "Netflix, Disney+, Hulu and streaming media rules list will use this DNS"
msgstr "自定义 Netflix、Disney+、Hulu 以及 “流媒体” 规则列表的 DNS 服务器"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:927
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:989
msgid "Next"
msgstr "下一页"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:768
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:778
msgid "No DNS answer records returned."
msgstr "无 DNS 应答记录"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:560
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:570
msgid "No data available"
msgstr "暂无数据"
@@ -783,19 +787,19 @@ msgstr "暂无数据"
msgid "No log data."
msgstr "无日志数据。"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:886
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:896
msgid "No query log entries found."
msgstr "未找到查询日志记录。"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:904
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:941
msgid "Page %d / %d (%d entries)"
msgstr "第 %d / %d 页(%d 条记录)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:438
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:448
msgid "Per-query speed"
msgstr "单次查询速度"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:821
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:831
msgid "Permit"
msgstr "放行"
@@ -832,7 +836,7 @@ msgstr "当剩余 TTL 小于此值(秒)时触发预取"
msgid "Prevent DNS Leaks"
msgstr "防止 DNS 泄漏"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:916
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:978
msgid "Previous"
msgstr "上一页"
@@ -851,7 +855,7 @@ msgstr "Quad9 公共 DNS149.112.112.112"
msgid "Quad9 Public DNS (9.9.9.9)"
msgstr "Quad9 公共 DNS9.9.9.9"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:811
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:821
msgid "Query Log Details"
msgstr "查询日志详情"
@@ -861,7 +865,7 @@ msgid ""
"consume more memory)"
msgstr "查询日志环形缓冲区容量(FIFO 覆盖,默认 2000,数值越大占用内存越多)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1067
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1129
msgid "Query logs cleared successfully."
msgstr "查询日志清除成功。"
@@ -869,7 +873,7 @@ msgstr "查询日志清除成功。"
msgid "RUNNING"
msgstr "运行中"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1098
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1160
msgid "Real-time Query Logs"
msgstr "实时查询日志"
@@ -901,7 +905,7 @@ msgstr "远程 DNS 服务器"
msgid "Ring Buffer Capacity"
msgstr "环形缓冲区容量"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:799
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:809
msgid "Rule Hit"
msgstr "命中规则"
@@ -921,7 +925,7 @@ msgstr "保存统计数据与查询日志在本地,并在下次启动时重新
msgid "Save the cache locally and reload the cache dump on the next startup"
msgstr "保存缓存到本地文件,以供下次启动时重新载入使用"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1015
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1077
msgid "Search domain or client IP..."
msgstr "搜索域名或客户端 IP..."
@@ -933,7 +937,7 @@ msgstr "设置日志文件的最大容量(单位:MB)。"
msgid "Starting update..."
msgstr "正在启动更新..."
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1094
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1156
#: luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json:38
msgid "Statistics"
msgstr "统计"
@@ -942,7 +946,7 @@ msgstr "统计"
msgid "Stats Dump"
msgstr "自动保存统计"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:896
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:956
msgid "Status"
msgstr "状态"
@@ -962,7 +966,7 @@ msgstr "支持本地文件,例如:file:///var/mosdns/example.txt"
msgid "TCP/DoT Connection Multiplexing"
msgstr "TCP/DoT 连接复用"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:723
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:733
msgid "Target file: %s (supports domain matching rules)"
msgstr "目标文件: %s (支持域名匹配规则)"
@@ -1002,20 +1006,20 @@ msgstr ""
"这是文件 “/etc/mosdns/config_custom.yaml” 的内容,您的 MosDNS 配置将从此文件"
"生成。仅接受 yaml 格式的配置内容。"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:791
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:893
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:801
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:953
msgid "Time"
msgstr "时间"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:599
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:609
msgid "Top Blocked Domains"
msgstr "拦截域名 (Top 10)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:606
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:616
msgid "Top Clients"
msgstr "活跃客户端 (Top 10)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:592
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:602
msgid "Top Queried Domains"
msgstr "查询域名 (Top 10)"
@@ -1065,7 +1069,7 @@ msgstr "更新成功"
msgid "Updating Database..."
msgstr "更新数据库..."
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:795
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:805
msgid "Upstream"
msgstr "上游服务器"
@@ -1091,8 +1095,8 @@ msgstr ""
"则)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:23
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:633
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:635
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:643
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:645
msgid "White Lists"
msgstr "白名单"
@@ -1110,24 +1114,15 @@ msgstr "信风公共 DNS114.114.115.115"
msgid "https://gh-proxy.com"
msgstr "https://gh-proxy.com"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:427
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:540
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:437
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:550
msgid "● Live"
msgstr "● 实时"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:223
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:233
msgid "● Live Auto-refresh"
msgstr "● 实时刷新"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:227
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:237
msgid "❚❚ Paused (Page %d)"
msgstr "❚❚ 暂停 (第 %d 页)"
#~ msgid "Action"
#~ msgstr "操作"
#~ msgid "Add %s to Blocklist"
#~ msgstr "将 %s 添加到黑名单"
#~ msgid "Add %s to Whitelist"
#~ msgstr "将 %s 添加到白名单"
+21 -1
View File
@@ -429,7 +429,11 @@ generate_config() {
json_add_string "exec" "\$modify_ddns_ttl"
json_close_object
json_add_object
json_add_string "matches" "!qname \$ddnslist"
json_add_array "matches"
json_add_string "" "!qname \$ddnslist"
json_add_string "" "!qname \$blocklist"
json_add_string "" "!qname \$adlist"
json_close_array
json_add_string "exec" "\$modify_ttl"
json_close_object
[ "$cloudflare" -eq 1 ] && {
@@ -571,6 +575,22 @@ generate_config() {
json_add_array "args"
json_add_object
json_add_string "matches" "qname \$blocklist"
json_add_string "exec" "black_hole 0.0.0.0 ::"
json_close_object
json_add_object
json_add_string "matches" "qname \$adlist"
json_add_string "exec" "black_hole 0.0.0.0 ::"
json_close_object
json_add_object
json_add_string "matches" "has_resp"
json_add_string "exec" "ttl 3600"
json_close_object
json_add_object
json_add_string "matches" "has_resp"
json_add_string "exec" "accept"
json_close_object
json_add_object
json_add_string "matches" "qname \$blocklist"
json_add_string "exec" "reject 3"
json_close_object
json_add_object
@@ -72,7 +72,7 @@ function get_adlist() {
let adblock = uci_cursor.get('mosdns', 'config', 'adblock');
if (adblock !== '1') {
mkdir('/etc/mosdns/rule', 0755);
mkdir('/var/mosdns', 0755);
exec_sys('rm -rf /etc/mosdns/rule/adlist /etc/mosdns/rule/.ad_source');
writefile('/var/mosdns/disable-ads.txt', '');
print("/var/mosdns/disable-ads.txt\n");
+9 -1
View File
@@ -7,7 +7,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=26.9.1
PKG_VERSION:=26.9.9
PKG_RELEASE:=1
PKG_PO_VERSION:=$(PKG_VERSION)
@@ -164,9 +164,17 @@ define Package/$(PKG_NAME)/conffiles
/usr/share/passwall/rules/domains_excluded
endef
define Package/$(PKG_NAME)/postinst
#!/bin/sh
rm -f $${IPKG_INSTROOT}/usr/share/passwall/rules/*.nft
rm -rf $${IPKG_INSTROOT}/tmp/etc/passwall_tmp
exit 0
endef
define Package/$(PKG_NAME)/postrm
#!/bin/sh
rm -f $${IPKG_INSTROOT}/usr/share/passwall/rules/*.nft
rm -rf $${IPKG_INSTROOT}/tmp/etc/passwall_tmp
exit 0
endef
@@ -61,14 +61,11 @@ function getOption(config, section, opt) {
const id = `cbid.${config}.${section}.${opt}`;
obj = document.getElementsByName(id)[0] || document.getElementById(id);
if (obj) {
//lua luci combobox
const combobox = document.getElementById('cbi.combobox.' + id);
if (combobox) {
obj.combobox = combobox;
}
const div = document.getElementById(id);
if (div && div.getElementsByTagName("li").length > 0) {
obj = div;
}
return obj;
} else {
return null;
@@ -479,7 +479,21 @@ function add_node()
uci_set(uid, "group", group)
end
uci_set(uid, "type", "Socks")
if api.is_finded("ipt2socks") then
uci_set(uid, "type", "Socks")
elseif api.finded_com("sing-box") then
uci_set(uid, "type", "sing-box")
elseif api.finded_com("xray") then
uci_set(uid, "type", "Xray")
elseif api.is_finded("sslocal") then
uci_set(uid, "type", "SS-Rust")
elseif api.is_finded("ssr-local") then
uci_set(uid, "type", "SSR")
elseif api.finded_com("hysteria") then
uci_set(uid, "type", "Hysteria2")
elseif api.is_finded("naive") then
uci_set(uid, "type", "Naiveproxy")
end
if redirect == "1" then
uci_save()
@@ -47,20 +47,23 @@ end
-- [[ ACLs Settings ]]--
s = m:section(NamedSection, arg[1], translate("ACLs"), translate("ACLs"))
s.addremove = false
s.dynamic = false
s:tab("Main", translate("Main"))
s:tab("Proxy", translate("Proxy"))
s:tab("DNS", translate("DNS"))
s:tab("Log", translate("Log"))
---- Enable
o = s:option(Flag, "enabled", translate("Enable"))
o = s:taboption("Main", Flag, "enabled", translate("Enable"))
o.default = 1
o.rmempty = false
---- Remarks
o = s:option(Value, "remarks", translate("Remarks"))
o = s:taboption("Main", Value, "remarks", translate("Remarks"))
o.default = arg[1]
o.rmempty = false
o = s:option(Value, "interface", translate("Source Interface"))
o = s:taboption("Main", Value, "interface", translate("Source Interface"))
o:value("", translate("All"))
local iface = api.get_network_devices()
for _, d in ipairs(iface) do
@@ -94,7 +97,7 @@ table.sort(mac_t, function(a,b)
end)
---- Source
sources = s:option(DynamicList, "sources", translate("Source"))
sources = s:taboption("Main", DynamicList, "sources", translate("Source"))
sources.description = "<ul><li>" .. translate("Example:")
.. "</li><li>" .. translate("MAC") .. ": 00:00:00:FF:FF:FF"
.. "</li><li>" .. translate("IP") .. ": 192.168.1.100"
@@ -152,13 +155,14 @@ sources.validate = function(self, value, t)
return value
end
o = s:option(ListValue, "mode", translate("Mode"))
local NODE = m:get("@global[0]", "node") or ""
o = s:taboption("Main", ListValue, "mode", translate("Mode"))
o:value("0", translate("No Proxy"))
o:value("1", translate("Proxy"))
---- TCP No Redir Ports
local TCP_NO_REDIR_PORTS = m:get("@global_forwarding[0]", "tcp_no_redir_ports")
o = s:option(Value, "tcp_no_redir_ports", translate("TCP No Redir Ports"))
o = s:taboption("Proxy", Value, "tcp_no_redir_ports", translate("TCP No Redir Ports"))
o:value("", translate("Use global config") .. "(" .. TCP_NO_REDIR_PORTS .. ")")
o:value("disable", translate("No patterns are used"))
o:value("1:65535", translate("All"))
@@ -167,7 +171,7 @@ o.validate = port_validate
---- UDP No Redir Ports
local UDP_NO_REDIR_PORTS = m:get("@global_forwarding[0]", "udp_no_redir_ports")
o = s:option(Value, "udp_no_redir_ports", translate("UDP No Redir Ports"),
o = s:taboption("Proxy", Value, "udp_no_redir_ports", translate("UDP No Redir Ports"),
"<font color='red'>" ..
translate("Fill in the ports you don't want to be forwarded by the agent, with the highest priority.") ..
"</font>")
@@ -177,53 +181,48 @@ o:value("1:65535", translate("All"))
o:depends("mode", "1")
o.validate = port_validate
o = s:option(DummyValue, "_hide_node_option", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o = s:taboption("Main", HideValue, "_hide_node_option", "")
o:depends("mode", "0")
o:depends({ tcp_no_redir_ports = "1:65535", udp_no_redir_ports = "1:65535" })
if TCP_NO_REDIR_PORTS == "1:65535" and UDP_NO_REDIR_PORTS == "1:65535" then
o:depends({ tcp_no_redir_ports = "", udp_no_redir_ports = "" })
end
o = s:option(Flag, "use_global_config", translatef("Use global config"))
o = s:taboption("Main", Flag, "use_global_config", translatef("Use global config"))
o.default = "0"
o.rmempty = false
o:depends({ _hide_node_option = "1", ['!reverse'] = true })
o:depends({ _hide_node_option = "1", ['!reverse'] = true })
o = s:option(ListValue, "node", "<a style='color: red'>" .. translate("Proxy Node") .. "</a>")
o = s:taboption("Main", ListValue, "node", "<a style='color: red'>" .. translate("Proxy Node") .. "</a>")
o.group = {}
o:depends({ _hide_node_option = false, use_global_config = false })
o.template = m:template_path("/cbi/nodes_listvalue")
o.description = translate("Use global configuration when using the global node.")
current_node_id = o:formvalue(arg[1])
if not current_node_id then
current_node_id = m:get(arg[1], "node")
current_node_id = m:get(arg[1], "node")
local node_value = s.fields["node"]:formvalue(s.section)
if node_value then
current_node_id = node_value
end
current_node = current_node_id and m:get(current_node_id) or {}
o = s:option(DummyValue, "_acl_node_bool", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o:depends({ node = "", ['!reverse'] = true })
o = s:taboption("Main", HideValue, "node_save_before", "")
o.value = current_node[".name"]
o.cbid = function(self, section) return "node_save_before" end
---- Log
o = s:option(Flag, "log", translate("Enable Node Log"))
o.default = 0
o.rmempty = false
o:depends({ _hide_node_option = false, use_global_config = false })
o = s:taboption("Main", HideValue, "_acl_node_bool", "")
o:depends({ node = "", ['!reverse'] = true })
o = s:option(ListValue, "loglevel", "Sing-Box/Xray " .. translate("Log Level"))
o.default = "warn"
o:value("debug", "Debug")
o:value("info", "Info")
o:value("warn", "Warning")
o:value("error", "Error")
o:depends("log", "1")
o = s:taboption("Main", HideValue, "_diff_global_node", "")
if NODE ~= "" then
o:depends({ node = NODE, ['!reverse'] = true })
else
o:depends({ _acl_node_bool = "1" })
end
---- TCP Proxy Drop Ports
local TCP_PROXY_DROP_PORTS = m:get("@global_forwarding[0]", "tcp_proxy_drop_ports")
o = s:option(Value, "tcp_proxy_drop_ports", translate("TCP Proxy Drop Ports"))
o = s:taboption("Proxy", Value, "tcp_proxy_drop_ports", translate("TCP Proxy Drop Ports"))
o:value("", translate("Use global config") .. "(" .. TCP_PROXY_DROP_PORTS .. ")")
o:value("disable", translate("No patterns are used"))
o.validate = port_validate
@@ -232,7 +231,7 @@ o:depends({ _acl_node_bool = "1" })
---- UDP Proxy Drop Ports
local UDP_PROXY_DROP_PORTS = m:get("@global_forwarding[0]", "udp_proxy_drop_ports")
o = s:option(Value, "udp_proxy_drop_ports", translate("UDP Proxy Drop Ports"))
o = s:taboption("Proxy", Value, "udp_proxy_drop_ports", translate("UDP Proxy Drop Ports"))
o:value("", translate("Use global config") .. "(" .. UDP_PROXY_DROP_PORTS .. ")")
o:value("disable", translate("No patterns are used"))
o:value("443", translate("QUIC"))
@@ -242,7 +241,7 @@ o:depends({ _acl_node_bool = "1" })
---- TCP Redir Ports
local TCP_REDIR_PORTS = m:get("@global_forwarding[0]", "tcp_redir_ports")
o = s:option(Value, "tcp_redir_ports", translate("TCP Redir Ports"), translatef("Only work with using the %s node.", "TCP"))
o = s:taboption("Proxy", Value, "tcp_redir_ports", translate("TCP Redir Ports"), translatef("Only work with using the %s node.", "TCP"))
o:value("", translate("Use global config") .. "(" .. TCP_REDIR_PORTS .. ")")
o:value("1:65535", translate("All"))
o:value("80,443", "80,443")
@@ -254,7 +253,7 @@ o:depends({ _acl_node_bool = "1" })
---- UDP Redir Ports
local UDP_REDIR_PORTS = m:get("@global_forwarding[0]", "udp_redir_ports")
o = s:option(Value, "udp_redir_ports", translate("UDP Redir Ports"), translatef("Only work with using the %s node.", "UDP"))
o = s:taboption("Proxy", Value, "udp_redir_ports", translate("UDP Redir Ports"), translatef("Only work with using the %s node.", "UDP"))
o:value("", translate("Use global config") .. "(" .. UDP_REDIR_PORTS .. ")")
o:value("1:65535", translate("All"))
o:value("53", "53")
@@ -262,7 +261,7 @@ o.validate = port_validate
o:depends({ use_global_config = true })
o:depends({ _acl_node_bool = "1" })
o = s:option(DummyValue, "tips", " ")
o = s:taboption("Proxy", DummyValue, "tips", " ")
o.rawhtml = true
o.cfgvalue = function(t, n)
return string.format('<font color="red">%s</font>',
@@ -271,71 +270,67 @@ end
o:depends({ use_global_config = true })
o:depends({ _acl_node_bool = "1" })
o = s:option(Flag, "use_direct_list", translatef("Use %s", translate("Direct List")))
o = s:taboption("Proxy", Flag, "use_direct_list", translatef("Use %s", translate("Direct List")))
o.default = "1"
o:depends({ _acl_node_bool = "1" })
o:depends({ _acl_node_bool = "1", _diff_global_node = "1" })
o = s:option(Flag, "use_proxy_list", translatef("Use %s", translate("Proxy List")))
o = s:taboption("Proxy", Flag, "use_proxy_list", translatef("Use %s", translate("Proxy List")))
o.default = "1"
o:depends({ _acl_node_bool = "1" })
o:depends({ _acl_node_bool = "1", _diff_global_node = "1" })
o = s:option(Flag, "use_block_list", translatef("Use %s", translate("Block List")))
o = s:taboption("Proxy", Flag, "use_block_list", translatef("Use %s", translate("Block List")))
o.default = "1"
o:depends({ _acl_node_bool = "1" })
o:depends({ _acl_node_bool = "1", _diff_global_node = "1" })
if has_gfwlist then
o = s:option(Flag, "use_gfw_list", translatef("Use %s", translate("GFW List")))
o = s:taboption("Proxy", Flag, "use_gfw_list", translatef("Use %s", translate("GFW List")))
o.default = "1"
o:depends({ _acl_node_bool = "1" })
o:depends({ _acl_node_bool = "1", _diff_global_node = "1" })
end
if has_chnlist or has_chnroute then
o = s:option(ListValue, "chn_list", translate("China List"))
o = s:taboption("Proxy", ListValue, "chn_list", translate("China List"))
o:value("0", translate("Close(Not use)"))
o:value("direct", translate("Direct Connection"))
o:value("proxy", translate("Proxy"))
o.default = "direct"
o:depends({ _acl_node_bool = "1" })
o:depends({ _acl_node_bool = "1", _diff_global_node = "1" })
end
o = s:option(ListValue, "tcp_proxy_mode", "TCP " .. translate("Proxy Mode"))
o = s:taboption("Proxy", ListValue, "tcp_proxy_mode", "TCP " .. translate("Proxy Mode"))
o:value("disable", translate("No Proxy"))
o:value("proxy", translate("Proxy"))
o:depends({ _acl_node_bool = "1" })
o:depends({ _acl_node_bool = "1", _diff_global_node = "1" })
o = s:option(ListValue, "udp_proxy_mode", "UDP " .. translate("Proxy Mode"))
o = s:taboption("Proxy", ListValue, "udp_proxy_mode", "UDP " .. translate("Proxy Mode"))
o:value("disable", translate("No Proxy"))
o:value("proxy", translate("Proxy"))
o:depends({ _acl_node_bool = "1" })
o:depends({ _acl_node_bool = "1", _diff_global_node = "1" })
o = s:option(DummyValue, "switch_mode", " ")
o = s:taboption("Proxy", DummyValue, "switch_mode", "")
o.template = m:template_path("/global/proxy")
o:depends({ _acl_node_bool = "1" })
o:depends({ _acl_node_bool = "1", _diff_global_node = "1" })
-- Node → DNS Depends Settings
o = s:option(DummyValue, "_node_sel_shunt", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o = s:taboption("Main", HideValue, "_node_sel_shunt", "")
o:depends({ node = "__always__" })
o = s:option(DummyValue, "_node_sel_other", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o:depends({ _node_sel_shunt = "1", ['!reverse'] = true })
o = s:taboption("Main", HideValue, "_node_sel_other", "")
o:depends({ _node_sel_shunt = "1", ['!reverse'] = true })
---- DNS
o = s:option(ListValue, "dns_shunt", "DNS " .. translate("Shunt"))
o = s:taboption("DNS", ListValue, "dns_shunt", "DNS " .. translate("Shunt"))
o.default = "chinadns-ng"
o:value("dnsmasq", "Dnsmasq")
o:value("chinadns-ng", translate("ChinaDNS-NG (recommended)"))
o:depends({ _acl_node_bool = "1" })
o:depends({ _acl_node_bool = "1", _diff_global_node = "1" })
o = s:option(Flag, "filter_proxy_ipv6", translate("Filter Proxy Host IPv6"), translate("Experimental feature."))
o = s:taboption("DNS", Flag, "filter_proxy_ipv6", translate("Filter Proxy Host IPv6"), translate("Experimental feature."))
o.default = "0"
o:depends({ _acl_node_bool = "1" })
o:depends({ _acl_node_bool = "1", _diff_global_node = "1" })
---- DNS Forward Mode
o = s:option(ListValue, "dns_mode", translate("Filter Mode"))
o = s:taboption("DNS", ListValue, "dns_mode", translate("Filter Mode"))
if api.is_finded("dns2socks") then
o:value("dns2socks", "dns2socks")
end
@@ -345,7 +340,7 @@ end
if has_xray then
o:value("xray", "Xray")
end
o:depends({ _acl_node_bool = "1", _node_sel_other = "1" })
o:depends({ _acl_node_bool = "1", _node_sel_other = "1", _diff_global_node = "1" })
o.write = function(self, section, value)
if value == "dns2socks" then
m:del(section, "v2ray_dns_mode")
@@ -374,7 +369,7 @@ o.remove = function(self, section)
end
end
o = s:option(ListValue, "xray_dns_mode", translate("Request protocol"))
o = s:taboption("DNS", ListValue, "xray_dns_mode", translate("Request protocol"))
o.default = "tcp"
o:value("tcp", "TCP")
o:value("udp", "UDP")
@@ -389,7 +384,7 @@ o.write = function(self, section, value)
end
end
o = s:option(ListValue, "singbox_dns_mode", translate("Request protocol"))
o = s:taboption("DNS", ListValue, "singbox_dns_mode", translate("Request protocol"))
o.default = "tcp"
o:value("tcp", "TCP")
o:value("udp", "UDP")
@@ -408,7 +403,7 @@ o.write = function(self, section, value)
end
---- DNS Forward
o = s:option(Value, "remote_dns", translate("Remote DNS"))
o = s:taboption("DNS", Value, "remote_dns", translate("Remote DNS"))
o.datatype = "or(ipaddr,ipaddrport(1))"
o.default = "1.1.1.1"
o:value("1.1.1.1", "1.1.1.1 (CloudFlare)")
@@ -427,7 +422,7 @@ o:depends({singbox_dns_mode = "tcp"})
o:depends({singbox_dns_mode = "tls"})
o:depends({singbox_dns_mode = "quic"})
o = s:option(Value, "remote_dns_doh", translate("Remote DNS DoH"))
o = s:taboption("DNS", Value, "remote_dns_doh", translate("Remote DNS DoH"))
o.description = translate("Format: URL[,IP] (optional IP to map the domain in the URL)")
o:value("https://1.1.1.1/dns-query", "1.1.1.1 (CloudFlare)")
o:value("https://1.1.1.2/dns-query", "1.1.1.2 (CloudFlare-Security)")
@@ -466,15 +461,15 @@ o:depends({xray_dns_mode = "doh"})
o:depends({singbox_dns_mode = "doh"})
o:depends({singbox_dns_mode = "http3"})
o = s:option(Value, "remote_dns_client_ip", translate("EDNS Client Subnet"))
o = s:taboption("DNS", Value, "remote_dns_client_ip", translate("EDNS Client Subnet"))
o.description = translate("Notify the DNS server when the DNS query is notified, the location of the client (cannot be a private IP address).") .. "<br />" ..
translate("This feature requires the DNS server to support the Edns Client Subnet (RFC7871).")
o.datatype = "ipaddr"
o:depends({dns_mode = "sing-box"})
o:depends({dns_mode = "xray"})
o:depends({_node_sel_shunt = "1"})
o:depends({_node_sel_shunt = "1", _diff_global_node = "1"})
o = s:option(Flag, "remote_fakedns", "FakeDNS", translate("Use FakeDNS work in the domain that proxy."))
o = s:taboption("DNS", Flag, "remote_fakedns", "FakeDNS", translate("Use FakeDNS work in the domain that proxy."))
o.default = "0"
o.rmempty = false
o:depends({dns_mode = "sing-box"})
@@ -492,12 +487,12 @@ o.validate = function(self, value, t)
return value
end
o = s:option(Value, "remote_rewrite_ttl", translate("Remote DNS") .. " TTL")
o = s:taboption("DNS", Value, "remote_rewrite_ttl", translate("Remote DNS") .. " TTL")
o.datatype = "min(1)"
o.default = "30"
o:depends({dns_mode = "sing-box"})
o = s:option(ListValue, "chinadns_ng_default_tag", translate("Default DNS"))
o = s:taboption("DNS", ListValue, "chinadns_ng_default_tag", translate("Default DNS"))
o.default = "none"
o:value("gfw", translate("Remote DNS"))
o:value("chn", translate("Direct DNS"))
@@ -515,18 +510,73 @@ o.description = desc
.. "</ul>"
o:depends({dns_shunt = "chinadns-ng", tcp_proxy_mode = "proxy", chn_list = "direct"})
o = s:option(Flag, "force_https_soa", translate("Force HTTPS SOA"), translate("Force queries with qtype 65 to respond with an SOA record."))
o = s:taboption("DNS", Flag, "force_https_soa", translate("Force HTTPS SOA"), translate("Force queries with qtype 65 to respond with an SOA record."))
o.default = "0"
o.rmempty = false
o:depends({dns_shunt = "chinadns-ng"})
o = s:option(ListValue, "use_default_dns", translate("Default DNS"))
o = s:taboption("DNS", ListValue, "use_default_dns", translate("Default DNS"))
o.default = "direct"
o:value("remote", translate("Remote DNS"))
o:value("direct", translate("Direct DNS"))
o.description = desc .. "</ul>"
o:depends({dns_shunt = "dnsmasq", tcp_proxy_mode = "proxy", chn_list = "direct"})
---- Log
o = s:taboption("Log", Flag, "log", translate("Enable Node Log"))
o.default = 0
o.rmempty = false
o:depends({ _acl_node_bool = "1", _diff_global_node = "1" })
o = s:taboption("Log", ListValue, "loglevel", "Sing-Box/Xray " .. translate("Log Level"))
o.default = "warn"
o:value("debug", "Debug")
o:value("info", "Info")
o:value("warn", "Warning")
o:value("error", "Error")
o:depends("log", "1")
o = s:taboption("Log", DummyValue, "_node_log", translate("Log File"))
o.rawhtml = true
o.cfgvalue = function(t, n)
local log_file = api.TMP_PATH .. "/acl/" .. arg[1] .. "/node.log"
local log_url = api.url("get_redir_log") .. "?id=" .. arg[1]
local s = "<code>%s</code>&nbsp;&nbsp;" % log_file
if api.fs.access(log_file) then
local btn = string.format(
'<input class="btn cbi-button cbi-button-apply" type="button" value="%s" onclick="window.open(\'%s\', \'_blank\')" />',
translate("View Log"),
log_url
)
s = s .. btn
end
return s
end
o:depends("log", "1")
o = s:taboption("Log", Flag, "log_chinadns_ng", translate("Enable") .. " ChinaDNS-NG " .. translate("Log"))
o.default = "0"
o.rmempty = false
o:depends("dns_shunt", "chinadns-ng")
o = s:taboption("Log", DummyValue, "_chinadns_ng_log", translate("Log File"))
o.rawhtml = true
o.cfgvalue = function(t, n)
local log_file = api.TMP_PATH .. "/acl/" .. arg[1] .. "/chinadns_ng.log"
local log_url = api.url("get_chinadns_log") .. "?flag=" .. arg[1]
local s = "<code>%s</code>&nbsp;&nbsp;" % log_file
if api.fs.access(log_file) then
local btn = string.format(
'<input class="btn cbi-button cbi-button-apply" type="button" value="%s" onclick="window.open(\'%s\', \'_blank\')" />',
translate("View Log"),
log_url
)
s = s .. btn
end
return s
end
o:depends("log_chinadns_ng", "1")
local o_node = s.fields["node"]
local shunt_list = {}
@@ -536,7 +586,7 @@ for k, v in pairs(socks_list) do
end
for k, v in pairs(nodes_table) do
if #normal_list == 0 then
s.fields["dns_mode"]:depends({ _acl_node_bool = "1" })
s.fields["dns_mode"]:depends({ _acl_node_bool = "1", _diff_global_node = "1" })
break
end
if v.protocol and v.protocol == "_shunt" then
@@ -544,16 +594,16 @@ for k, v in pairs(nodes_table) do
o_node:value(v.id, v["remark"])
o_node.group[#o_node.group+1] = (v.group and v.group ~= "") and v.group or translate("default")
s.fields["xray_dns_mode"]:depends({ _acl_node_bool = "1", node = v.id })
s.fields["xray_dns_mode"]:depends({ _acl_node_bool = "1", _diff_global_node = "1", node = v.id })
s.fields["_node_sel_shunt"]:depends({ node = v.id })
end
if v.type == "sing-box" and has_singbox then
o_node:value(v.id, v["remark"])
o_node.group[#o_node.group+1] = (v.group and v.group ~= "") and v.group or translate("default")
s.fields["singbox_dns_mode"]:depends({ _acl_node_bool = "1", node = v.id })
s.fields["singbox_dns_mode"]:depends({ _acl_node_bool = "1", _diff_global_node = "1", node = v.id })
s.fields["_node_sel_shunt"]:depends({ node = v.id })
s.fields["remote_rewrite_ttl"]:depends({ _acl_node_bool = "1", node = v.id })
s.fields["remote_rewrite_ttl"]:depends({ _acl_node_bool = "1", _diff_global_node = "1", node = v.id })
end
shunt_list[#shunt_list + 1] = v
else
@@ -562,21 +612,17 @@ for k, v in pairs(nodes_table) do
end
end
m:appendTemplate("/acl/config_footer", {section = arg[1]})
--[[
-- Shunt
if current_node.protocol == "_shunt" then
if current_node.protocol == "_shunt" and current_node[".name"] ~= NODE then
local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall/client/include/shunt_options.lua")
setfenv(shunt_lua, getfenv(1))(m, s, {
s_cfgid = s.section,
node_id = current_node_id,
node = current_node,
verify_option = s.fields["node"]
verify_option = s.fields["node"],
tab = "Shunt",
tab_desc = translate("Shunt Rule"),
})
end
m:appendTemplate("/acl/shunt", { shunt_list = api.jsonc.stringify(shunt_list), section = s.section })
]]--
m:appendTemplate("/include/node_change", { verify_option = s.fields["node"], shunt_list = api.jsonc.stringify(shunt_list) })
return api.return_map(m)
@@ -112,6 +112,10 @@ o:value("", translate("Close"))
o.group = {""}
current_node_id = m:get(s.section, "node")
local node_value = s.fields["node"]:formvalue(s.section)
if node_value then
current_node_id = node_value
end
current_node = current_node_id and m:get(current_node_id) or {}
-- Shunt Start
@@ -120,17 +124,10 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
if current_node.protocol == "_shunt" then
local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall/client/include/shunt_options.lua")
setfenv(shunt_lua, getfenv(1))(m, s, {
s_cfgid = s.section,
node_id = current_node_id,
node = current_node,
socks_list = socks_list,
urltest_list = urltest_list,
balancing_list = balancing_list,
iface_list = iface_list,
normal_list = normal_list,
verify_option = s.fields["node"],
tab = "Shunt",
tab_desc = translate("Shunt Rule")
tab_desc = translate("Shunt Rule"),
})
end
else
@@ -165,20 +162,18 @@ o = s:taboption("Main", Flag, "node_socks_bind_local", translate("Node") .. " So
o.default = "1"
o:depends("_node", "1")
o = s:taboption("Main", DummyValue, "_node", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o = s:taboption("Main", HideValue, "node_save_before", "")
o.value = current_node[".name"]
o.cbid = function(self, section) return "node_save_before" end
o = s:taboption("Main", HideValue, "_node", "")
o:depends({ node = "", ['!reverse'] = true })
-- Node → DNS Depends Settings
o = s:taboption("Main", DummyValue, "_node_sel_shunt", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o = s:taboption("Main", HideValue, "_node_sel_shunt", "")
o:depends({ node = "__always__" })
o = s:taboption("Main", DummyValue, "_node_sel_other", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o = s:taboption("Main", HideValue, "_node_sel_other", "")
o:depends({ _node_sel_shunt = "1", ['!reverse'] = true })
-- [[ DNS Settings ]]--
@@ -565,7 +560,7 @@ o:value("disable", translate("No Proxy"))
o:value("proxy", translate("Proxy"))
o.default = "proxy"
o = s:taboption("Proxy", DummyValue, "switch_mode", " ")
o = s:taboption("Proxy", DummyValue, "switch_mode", "")
o.template = m:template_path("/global/proxy")
---- Check the transparent proxy component
@@ -617,21 +612,46 @@ o:value("info", "Info")
o:value("warn", "Warning")
o:value("error", "Error")
o = s:taboption("log", Flag, "advanced_log_feature", translate("Advanced log feature"), translate("For professionals only."))
o.default = "0"
o = s:taboption("log", Flag, "sys_log", translate("Logging to system log"), translate("Logging to the system log for more advanced functions. For example, send logs to a dedicated log server."))
o:depends("advanced_log_feature", "1")
o.default = "0"
o = s:taboption("log", Value, "persist_log_path", translate("Persist log file directory"), translate("The path to the directory used to store persist log files, the \"/\" at the end can be omitted. Leave it blank to disable this feature."))
o:depends({ ["advanced_log_feature"] = 1, ["sys_log"] = 0 })
o = s:taboption("log", Value, "log_event_filter", translate("Log Event Filter"), translate("Support regular expression."))
o:depends("advanced_log_feature", "1")
o = s:taboption("log", Value, "log_event_cmd", translate("Shell Command"), translate("Shell command to execute, replace log content with %s."))
o:depends("advanced_log_feature", "1")
o = s:taboption("log", DummyValue, "_node_log", translate("Log File"))
o.rawhtml = true
o.cfgvalue = function(t, n)
local log_file = api.TMP_PATH .. "/acl/default/global.log"
local log_url = api.url("get_redir_log") .. "?id=default"
local s = "<code>%s</code>&nbsp;&nbsp;" % log_file
if api.fs.access(log_file) then
local btn = string.format(
'<input class="btn cbi-button cbi-button-apply" type="button" value="%s" onclick="window.open(\'%s\', \'_blank\')" />',
translate("View Log"),
log_url
)
s = s .. btn
end
return s
end
o:depends("log_node", "1")
o = s:taboption("log", Flag, "log_chinadns_ng", translate("Enable") .. " ChinaDNS-NG " .. translate("Log"))
o.default = "0"
o.rmempty = false
o:depends("dns_shunt", "chinadns-ng")
o = s:taboption("log", DummyValue, "_chinadns_ng_log", translate("Log File"))
o.rawhtml = true
o.cfgvalue = function(t, n)
local log_file = api.TMP_PATH .. "/acl/default/chinadns_ng.log"
local log_url = api.url("get_chinadns_log") .. "?flag=default"
local s = "<code>%s</code>&nbsp;&nbsp;" % log_file
if api.fs.access(log_file) then
local btn = string.format(
'<input class="btn cbi-button cbi-button-apply" type="button" value="%s" onclick="window.open(\'%s\', \'_blank\')" />',
translate("View Log"),
log_url
)
s = s .. btn
end
return s
end
o:depends("log_chinadns_ng", "1")
o = s:taboption("log", DummyValue, "_log_tips", " ")
o.rawhtml = true
@@ -647,6 +667,8 @@ s:tab("maintain", translate("Maintain"))
o = s:taboption("maintain", DummyValue, "")
o.template = m:template_path("/global/backup")
m:appendTemplate("/include/node_change", { verify_option = s.fields["node"], shunt_list = api.jsonc.stringify(shunt_list) })
-- [[ Socks Server ]]--
o = s:taboption("Main", Flag, "socks_enabled", "Socks " .. translate("Main switch"))
o.rmempty = false
@@ -784,7 +806,7 @@ for k, v in pairs(nodes_table) do
end
end
m:appendTemplate("/global/footer", {shunt_list = api.jsonc.stringify(shunt_list)})
m:appendTemplate("/global/footer")
m:appendTemplate("/cbi/sortable", {sectiontype = s2.sectiontype})
@@ -1,13 +1,28 @@
local m, s, data = ...
if not data.node_id or not data.node then
if not data.node or not data.node[".name"] then
return
end
local api = m.api
local s_cfgid = data.s_cfgid
local current_node_id = data.node_id
local section_id = s.section
local current_node = data.node
local current_node_id = current_node[".name"]
local verify_option = data.verify_option
local node_list = data.node_list or api.get_node_list()
local shunt_section_type = "shunt_option_list"
if data.tab then
if #s.tab_names > 0 then
table.insert(s.tab_names, 2, data.tab)
s.tabs[data.tab] = {
title = data.tab_desc,
childs = { }
}
else
s:tab(data.tab, data.tab_desc)
end
end
local groups = {}
m:foreach("shunt_rules", function(s)
@@ -16,6 +31,9 @@ m:foreach("shunt_rules", function(s)
end
end)
local node_save_before = luci.http.formvalue("node_save_before")
local load_shunt = luci.http.formvalue("load_shunt")
local function get_cfgvalue()
return function(self, section)
return m:get(current_node_id, self.option)
@@ -23,8 +41,10 @@ local function get_cfgvalue()
end
local function get_write()
return function(self, section, value)
if data.verify_option then
if data.verify_option:formvalue(section) == current_node_id then
if node_save_before and node_save_before ~= current_node_id then return end
if load_shunt == "1" then return end
if verify_option then
if verify_option:formvalue(section) == current_node_id then
m:set(current_node_id, self.option, value)
end
else
@@ -34,8 +54,10 @@ local function get_write()
end
local function get_remove()
return function(self, section)
if data.verify_option then
if data.verify_option:formvalue(section) == current_node_id then
if node_save_before and node_save_before ~= current_node_id then return end
if load_shunt == "1" then return end
if verify_option then
if verify_option:formvalue(section) == current_node_id then
m:del(current_node_id, self.option)
end
else
@@ -44,10 +66,6 @@ local function get_remove()
end
end
if data.tab then
s:tab(data.tab, data.tab_desc)
end
local function add_option(class, option_name, option_title, option_desc)
local a
if data.tab then
@@ -63,8 +81,8 @@ local function add_option(class, option_name, option_title, option_desc)
a.write = get_write()
a.remove = get_remove()
end
if data.verify_option then
a:depends(data.verify_option.option, current_node_id)
if verify_option then
a:depends(verify_option.option, current_node_id)
end
return a
end
@@ -81,7 +99,7 @@ local function add_depends(o, deps)
end
end
if data.node.type == "Xray" then
if current_node.type == "Xray" then
o = add_option(ListValue, "domainStrategy", translate("Domain Strategy"))
o:value("AsIs")
o:value("IPIfNonMatch")
@@ -134,7 +152,7 @@ table.insert(shunt_rules, {
s2 = m:section(Table, shunt_rules, " ")
s2.config = m.config
s2.sectiontype = "shunt_option_list"
s2.sectiontype = shunt_section_type
o = s2:option(DummyValue, "remarks", translate("Rule"))
o.rawhtml = true
@@ -157,9 +175,13 @@ _node.cfgvalue = function(self, section)
return m:get(current_node_id, shunt_rules[section]["_node_option"]) or shunt_rules[section]["_node_default"]
end
_node.write = function(self, section, value)
if node_save_before and node_save_before ~= current_node_id then return end
if load_shunt == "1" then return end
return m:set(current_node_id, shunt_rules[section]["_node_option"], value)
end
_node.remove = function(self, section)
if node_save_before and node_save_before ~= current_node_id then return end
if load_shunt == "1" then return end
return m:del(current_node_id, shunt_rules[section]["_node_option"])
end
@@ -170,9 +192,13 @@ o.cfgvalue = function(self, section)
return m:get(current_node_id, shunt_rules[section]["_fakedns_option"])
end
o.write = function(self, section, value)
if node_save_before and node_save_before ~= current_node_id then return end
if load_shunt == "1" then return end
return m:set(current_node_id, shunt_rules[section]["_fakedns_option"], value)
end
o.remove = function(self, section)
if node_save_before and node_save_before ~= current_node_id then return end
if load_shunt == "1" then return end
return m:del(current_node_id, shunt_rules[section]["_fakedns_option"])
end
@@ -186,9 +212,13 @@ proxy_tag_node.cfgvalue = function(self, section)
return m:get(current_node_id, shunt_rules[section]["_proxy_tag_option"])
end
proxy_tag_node.write = function(self, section, value)
if node_save_before and node_save_before ~= current_node_id then return end
if load_shunt == "1" then return end
return m:set(current_node_id, shunt_rules[section]["_proxy_tag_option"], value)
end
proxy_tag_node.remove = function(self, section)
if node_save_before and node_save_before ~= current_node_id then return end
if load_shunt == "1" then return end
return m:del(current_node_id, shunt_rules[section]["_proxy_tag_option"])
end
@@ -204,4 +234,11 @@ for k1, v1 in pairs(node_list) do
end
end
m:appendTemplate("/include/shunt_options", {id = current_node_id, s_cfgid = s_cfgid or current_node_id, normal_list = api.jsonc.stringify(node_list.normal_list)})
m:appendTemplate("/include/shunt_options", {
node_id = current_node_id,
section_id = section_id or current_node_id,
section_type = shunt_section_type,
tab_name = data.tab,
normal_list = api.jsonc.stringify(node_list.normal_list),
shunt_list = api.jsonc.stringify(node_list.shunt_list)
})
@@ -61,7 +61,6 @@ end
local types_dir = "/usr/lib/lua/luci/model/cbi/" .. api.appname .. "/client/type/"
s.val = {}
s.val["type"] = m:get(arg[1], "type")
s.val["protocol"] = m:get(arg[1], "protocol")
if luci.http.formvalue("cbi.submit") == "1" then
local formvalue_type = luci.http.formvalue(formvalue_key .. "type")
@@ -72,34 +71,6 @@ end
o = s:option(ListValue, "type", translate("Type"))
if api.is_finded("ipt2socks") then
local type_name = "Socks"
s.fields["type"]:value(type_name, "Socks")
if s.val["type"] == type_name then
local s2 = NamedSection(m, arg[1], "server")
s2.type_name = type_name
s2.option_prefix = "socks_"
o = s2:option(ListValue, "del_protocol", " ") --始终隐藏,用于删除 protocol
o:depends({ __hide = "1" })
o.rewrite_option = "protocol"
o = s2:option(Value, "address", translate("Address (Support Domain Name)"))
o = s2:option(Value, "port", translate("Port"))
o.datatype = "port"
o = s2:option(Value, "username", translate("Username"))
o = s2:option(Value, "password", translate("Password"))
o.password = true
api.luci_types(s, s2)
end
end
local type_table = {}
for filename in api.fs.dir(types_dir) do
table.insert(type_table, filename)
@@ -0,0 +1,35 @@
if not api.is_finded("ipt2socks") then
return
end
-- [[ Socks ]]
local m, s1 = ...
local type_name = "Socks"
s1.fields["type"]:value(type_name, "Socks")
if s1.val["type"] and s1.val["type"] ~= type_name then
return
end
local s = NamedSection(m, arg[1], "tmp_" .. s1.sectiontype)
s.parent = s1
s.type_name = type_name
s.option_prefix = "socks_"
api.set_type_cbi(s)
o = s:option(ListValue, "del_protocol", "¡¡") --ʼÖÕÒþ²Ø£¬ÓÃÓÚɾ³ý protocol
o:depends({ __hide = "1" })
o.rewrite_option = "protocol"
o = s:option(Value, "address", translate("Address (Support Domain Name)"))
o = s:option(Value, "port", translate("Port"))
o.datatype = "port"
o = s:option(Value, "username", translate("Username"))
o = s:option(Value, "password", translate("Password"))
o.password = true
api.type_cbi_section(s1, s)
@@ -17,15 +17,11 @@ if s1.val["type"] ~= type_name then
return
end
local s = NamedSection(m, arg[1], "server")
local s = NamedSection(m, arg[1], "tmp_" .. s1.sectiontype)
s.parent = s1
s.type_name = type_name
s.option_prefix = "singbox_"
local formvalue_proto = luci.http.formvalue(formvalue_key .. "protocol")
if formvalue_proto then s1.val["protocol"] = formvalue_proto end
local arg_select_proto = luci.http.formvalue("select_proto") or ""
api.set_type_cbi(s)
local ss_method_new_list = {
"none", "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
@@ -72,24 +68,20 @@ end
o:value("_urltest", translate("URLTest"))
o:value("_shunt", translate("Shunt"))
o:value("_iface", translate("Custom Interface"))
function o.custom_cfgvalue(self, section)
if arg_select_proto ~= "" then
return arg_select_proto
else
return m:get(section, self.config_option)
end
local protocol_val = m:get(s.section, "protocol")
local formvalue_proto = s.fields["protocol"]:formvalue(s.section)
if formvalue_proto then
protocol_val = formvalue_proto
end
local load_urltest_options = s1.val["protocol"] == "_urltest" or arg_select_proto == "_urltest"
local load_shunt_options = s1.val["protocol"] == "_shunt" or arg_select_proto == "_shunt"
local load_iface_options = s1.val["protocol"] == "_iface" or arg_select_proto == "_iface"
local load_urltest_options = protocol_val == "_urltest"
local load_shunt_options = protocol_val == "_shunt"
local load_iface_options = protocol_val == "_iface"
local load_normal_options = true
if load_urltest_options or load_shunt_options or load_iface_options then
load_normal_options = nil
end
if not arg_select_proto:find("_") then
load_normal_options = true
end
local netdev_list = api.get_network_devices()
local node_list = api.get_node_list()
@@ -115,11 +107,11 @@ if load_urltest_options then -- [[ URLTest Start ]]
end
end
-- 读取旧 DynamicList
function o.custom_cfgvalue(self, section)
function o.cfgvalue(self, section)
return table.concat(m:get(section, "urltest_node") or {}, " ")
end
-- 写入保持 DynamicList
function o.custom_write(self, section, value)
function o.write(self, section, value)
local old = m:get(section, "urltest_node") or {}
local new, set = {}, {}
for v in value:gmatch("%S+") do
@@ -628,6 +620,7 @@ if singbox_tags:find("with_utls") then
o:depends({ protocol = "shadowsocks", tls = true })
o:depends({ protocol = "trojan", tls = true })
o:depends({ protocol = "anytls", tls = true })
o:depends({ protocol = "http", tls = true })
o = s:option(Value, "reality_publicKey", translate("Public Key"))
o:depends({ reality = true })
@@ -935,7 +928,7 @@ if not load_shunt_options then
for k1, v1 in pairs(node_list) do
if k1 ~= "shunt_list" and k1 ~= "iface_list" then
for i, v in ipairs(v1) do
if v.id ~= arg[1] then
if v.id ~= s.section then
o1:value(v.id, v.remark)
o1.group[#o1.group+1] = (v.group and v.group ~= "") and v.group or translate("default")
if k1 == "normal_list" then
@@ -949,14 +942,15 @@ if not load_shunt_options then
end
end
api.luci_types(s1, s)
api.type_cbi_section(s1, s)
if load_shunt_options then
local current_node = m:get(arg[1]) or {}
local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall/client/include/shunt_options.lua")
setfenv(shunt_lua, getfenv(1))(m, s1, {
node_id = arg[1],
node = current_node,
node = {
[".name"] = s.section,
type = type_name,
},
node_list = node_list,
})
end
@@ -15,15 +15,11 @@ if s1.val["type"] ~= type_name then
return
end
local s = NamedSection(m, arg[1], "server")
local s = NamedSection(m, arg[1], "tmp_" .. s1.sectiontype)
s.parent = s1
s.type_name = type_name
s.option_prefix = "xray_"
local formvalue_proto = luci.http.formvalue(formvalue_key .. "protocol")
if formvalue_proto then s1.val["protocol"] = formvalue_proto end
local arg_select_proto = luci.http.formvalue("select_proto") or ""
api.set_type_cbi(s)
local ss_method_list = {
"aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
@@ -53,31 +49,26 @@ if api.compare_versions(xray_version, ">=", "1.8.12") then
end
o:value("_shunt", translate("Shunt"))
o:value("_iface", translate("Custom Interface"))
function o.custom_cfgvalue(self, section)
if arg_select_proto ~= "" then
return arg_select_proto
else
return m:get(section, self.config_option)
end
local protocol_val = m:get(s.section, "protocol")
local formvalue_proto = s.fields["protocol"]:formvalue(s.section)
if formvalue_proto then
protocol_val = formvalue_proto
end
local load_balancing_options = s1.val["protocol"] == "_balancing" or arg_select_proto == "_balancing"
local load_shunt_options = s1.val["protocol"] == "_shunt" or arg_select_proto == "_shunt"
local load_iface_options = s1.val["protocol"] == "_iface" or arg_select_proto == "_iface"
local load_balancing_options = protocol_val == "_balancing"
local load_shunt_options = protocol_val == "_shunt"
local load_iface_options = protocol_val == "_iface"
local load_normal_options = true
if load_balancing_options or load_shunt_options or load_iface_options then
load_normal_options = nil
end
if not arg_select_proto:find("_") then
load_normal_options = true
end
local netdev_list = api.get_network_devices()
local node_list = api.get_node_list()
local fallback_list = {}
local is_balancer = nil
for k, e in ipairs(node_list.balancing_list or {}) do
if e.id ~= arg[1] then
if e.id ~= s.section then
fallback_list[#fallback_list + 1] = {
id = e["id"],
remark = e["remark"],
@@ -110,11 +101,11 @@ if load_balancing_options then -- [[ Load balancing Start ]]
end
end
-- 读取旧 DynamicList
function o.custom_cfgvalue(self, section)
function o.cfgvalue(self, section)
return table.concat(m:get(section, "balancing_node") or {}, " ")
end
-- 写入保持 DynamicList
function o.custom_write(self, section, value)
function o.write(self, section, value)
local old = m:get(section, "balancing_node") or {}
local new, set = {}, {}
for v in value:gmatch("%S+") do
@@ -213,7 +204,7 @@ if load_balancing_options then -- [[ Load balancing Start ]]
local depth = get_fallback_depth(v.id)
-- 超过最大套娃层数后,不允许继续选择 balancer
if depth < MAX_FALLBACK_DEPTH
and not will_loop(arg[1], v.id)
and not will_loop(s.section, v.id)
then
o:value(v.id, v.remark)
o.group[#o.group + 1] = (v.group and v.group ~= "") and v.group or translate("default")
@@ -410,6 +401,7 @@ o = s:option(Flag, "tls", translate("TLS"))
o.default = 0
o:depends({ protocol = "vmess" })
o:depends({ protocol = "vless" })
o:depends({ protocol = "http" })
o:depends({ protocol = "trojan" })
o:depends({ protocol = "shadowsocks" })
@@ -678,13 +670,13 @@ o.validate = function(self, value)
if v then return v end
return nil, "XHTTP Extra " .. translate("Must be JSON text!")
end
o.custom_cfgvalue = function(self, section, value)
o.cfgvalue = function(self, section, value)
local raw = m:get(section, "xhttp_extra")
if raw then
return api.base64Decode(raw)
end
end
o.custom_write = function(self, section, value)
o.write = function(self, section, value)
m:set(section, "xhttp_extra", api.base64Encode(value) or "")
local success, data = pcall(api.jsonc.parse, value)
if success and data then
@@ -700,7 +692,7 @@ o.custom_write = function(self, section, value)
m:del(section, "download_address")
end
end
o.custom_remove = function(self, section, value)
o.remove = function(self, section, value)
m:del(section, "xhttp_extra")
m:del(section, "download_address")
end
@@ -728,6 +720,8 @@ o:depends({ protocol = "vless", transport = "raw" })
o:depends({ protocol = "vless", transport = "ws" })
o:depends({ protocol = "vless", transport = "grpc" })
o:depends({ protocol = "vless", transport = "httpupgrade" })
o:depends({ protocol = "http" })
o:depends({ protocol = "socks" })
o:depends({ protocol = "shadowsocks" })
o:depends({ protocol = "trojan" })
@@ -763,13 +757,13 @@ o.validate = function(self, value)
if v then return v end
return nil, "FinalMask " .. translate("Must be JSON text!")
end
o.custom_cfgvalue = function(self, section, value)
o.cfgvalue = function(self, section, value)
local raw = m:get(section, "finalmask")
if raw then
return api.base64Decode(raw)
end
end
o.custom_write = function(self, section, value)
o.write = function(self, section, value)
m:set(section, "finalmask", api.base64Encode(value) or "")
end
@@ -871,7 +865,7 @@ if not load_shunt_options then
for k1, v1 in pairs(node_list) do
if k1 ~= "shunt_list" and k1 ~= "iface_list" then
for i, v in ipairs(v1) do
if v.id ~= arg[1] then
if v.id ~= s.section then
o1:value(v.id, v.remark)
o1.group[#o1.group+1] = (v.group and v.group ~= "") and v.group or translate("default")
if k1 == "normal_list" then
@@ -885,14 +879,16 @@ if not load_shunt_options then
end
end
api.luci_types(s1, s)
api.type_cbi_section(s1, s)
if load_shunt_options then
local current_node = m:get(arg[1]) or {}
local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall/client/include/shunt_options.lua")
setfenv(shunt_lua, getfenv(1))(m, s1, {
node_id = arg[1],
node = current_node,
node = {
[".name"] = s.section,
type = type_name,
},
node_list = node_list,
})
end
@@ -12,9 +12,11 @@ if s1.val["type"] ~= type_name then
return
end
local s = NamedSection(m, arg[1], "server")
local s = NamedSection(m, arg[1], "tmp_" .. s1.sectiontype)
s.parent = s1
s.type_name = type_name
s.option_prefix = "ssrust_"
api.set_type_cbi(s)
local ssrust_encrypt_method_list = {
"none", "plain",
@@ -69,4 +71,4 @@ end
o = s:option(Value, "plugin_opts", translate("opts"))
o:depends({ plugin_enabled = true })
api.luci_types(s1, s)
api.type_cbi_section(s1, s)
@@ -12,9 +12,11 @@ if s1.val["type"] ~= type_name then
return
end
local s = NamedSection(m, arg[1], "server")
local s = NamedSection(m, arg[1], "tmp_" .. s1.sectiontype)
s.parent = s1
s.type_name = type_name
s.option_prefix = "ssr_"
api.set_type_cbi(s)
local ssr_encrypt_method_list = {
"none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb",
@@ -63,4 +65,4 @@ o.default = 300
o = s:option(Flag, "tcp_fast_open", "TCP " .. translate("Fast Open"), translate("Need node support required"))
o.default = 0
api.luci_types(s1, s)
api.type_cbi_section(s1, s)
@@ -12,13 +12,11 @@ if s1.val["type"] ~= type_name then
return
end
local s = NamedSection(m, arg[1], "server")
local s = NamedSection(m, arg[1], "tmp_" .. s1.sectiontype)
s.parent = s1
s.type_name = type_name
s.option_prefix = "hysteria2_"
local function _n(name)
return s.option_prefix .. name
end
api.set_type_cbi(s)
o = s:option(ListValue, "protocol", translate("Protocol"))
o:value("udp", "UDP")
@@ -32,22 +30,22 @@ o:depends({ realms = false })
o = s:option(Value, "hop", translate("Port hopping range"))
o.description = translate("Format as 1000:2000 or 1000-2000 Multiple groups are separated by commas (,).")
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o:depends({ realms = false })
o = s:option(Value, "hop_interval", translate("Hop Interval(second)"), translate("Supports a fixed value or a random range (e.g., 30, 5-30), minimum 5."))
o.datatype = "or(uinteger,portrange)"
o.placeholder = "30"
o.default = "30"
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o:depends({ realms = false })
o = s:option(Flag, "realms", translate("Realms"))
o.default = "0"
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o = s:option(Value, "realm_url", translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o:depends({ realms = "1" })
o.validate = function(self, value)
value = api.trim(value)
@@ -58,26 +56,26 @@ end
o = s:option(DynamicList, "realm_stun", translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o:depends({ realms = "1" })
o = s:option(Flag, "realm_upnp", translate("Enable") .. " UPnP/NAT-PMP", translate("Enable UPnP/NAT-PMP port mapping on your gateway to improve hole punching success."))
o.default = "0"
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o:depends({ realms = "1" })
o = s:option(Value, "auth_password", translate("Auth Password"))
o.password = true
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o = s:option(ListValue, "obfs_type", translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:value("gecko")
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o = s:option(Value, "obfs_password", translate("Obfs Password"))
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o:depends({ obfs_type = "salamander" })
o:depends({ obfs_type = "gecko" })
@@ -86,14 +84,14 @@ o.datatype = "uinteger"
o.placeholder = "512"
o.default = "512"
o:depends({ obfs_type = "gecko" })
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o = s:option(Value, "obfs_MaxPacketSize", translate("Gecko Packet Size (max)"))
o.datatype = "uinteger"
o.placeholder = "1200"
o.default = "1200"
o:depends({ obfs_type = "gecko" })
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o = s:option(Flag, "fast_open", translate("Fast Open"))
o.default = "0"
@@ -106,28 +104,28 @@ o.default = "0"
o = s:option(Value, "tls_pinSHA256", translate("PinSHA256"),translate("Certificate fingerprint"))
o = s:option(Value, "up_mbps", translate("Max upload Mbps"))
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o = s:option(Value, "down_mbps", translate("Max download Mbps"))
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o = s:option(Value, "recv_window", translate("QUIC stream receive window"))
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o = s:option(Value, "recv_window_conn", translate("QUIC connection receive window"))
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o = s:option(Value, "idle_timeout", translate("Idle Timeout"), translate("Units:seconds") .. " (4~120)")
o.datatype = "range(4,120)"
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o = s:option(Value, "keep_alive_period", translate("QUIC KeepAlive interval"), translate("Units:seconds") .. " (2~60)")
o.datatype = "range(2,60)"
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o = s:option(Flag, "disable_mtu_discovery", translate("Disable MTU detection"))
o.default = "0"
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o = s:option(Flag, "ech", translate("ECH"))
@@ -145,6 +143,6 @@ end
o = s:option(Flag, "lazy_start", translate("Lazy Start"))
o.default = "0"
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
api.luci_types(s1, s)
api.type_cbi_section(s1, s)
@@ -12,9 +12,11 @@ if s1.val["type"] ~= type_name then
return
end
local s = NamedSection(m, arg[1], "server")
local s = NamedSection(m, arg[1], "tmp_" .. s1.sectiontype)
s.parent = s1
s.type_name = type_name
s.option_prefix = "naive_"
api.set_type_cbi(s)
o = s:option(ListValue, "protocol", translate("Protocol"))
o:value("https", translate("HTTPS"))
@@ -30,4 +32,4 @@ o = s:option(Value, "username", translate("Username"))
o = s:option(Value, "password", translate("Password"))
o.password = true
api.luci_types(s1, s)
api.type_cbi_section(s1, s)
@@ -15,9 +15,11 @@ if s1.val["type"] and s1.val["type"] ~= type_name then
return
end
local s = NamedSection(m, arg[1], "server")
local s = NamedSection(m, arg[1], "tmp_" .. s1.sectiontype)
s.parent = s1
s.type_name = type_name
s.option_prefix = "socks_"
api.set_type_cbi(s)
o = s:option(Value, "port", translate("Listen Port"))
o.datatype = "port"
@@ -44,4 +46,4 @@ o:depends({ firewall_allow = true })
o = s:option(Flag, "log", translate("Log"))
o.default = "1"
api.luci_types(s1, s)
api.type_cbi_section(s1, s)
@@ -21,9 +21,11 @@ if s1.val["type"] and s1.val["type"] ~= type_name then
return
end
local s = NamedSection(m, arg[1], "server")
local s = NamedSection(m, arg[1], "tmp_" .. s1.sectiontype)
s.parent = s1
s.type_name = type_name
s.option_prefix = "singbox_"
api.set_type_cbi(s)
local singbox_tags = luci.sys.exec(singbox_bin .. " version | grep 'Tags:' | awk '{print $2}'")
@@ -48,13 +50,13 @@ o.validate = function(self, value)
if v then return v end
return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!")
end
o.custom_cfgvalue = function(self, section, value)
o.cfgvalue = function(self, section, value)
local config_str = m:get(section, "config_str")
if config_str then
return api.base64Decode(config_str)
end
end
o.custom_write = function(self, section, value)
o.write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "")
end
@@ -562,4 +564,4 @@ o:value("warn")
o:value("error")
o:depends({ log = true })
api.luci_types(s1, s)
api.type_cbi_section(s1, s)
@@ -19,9 +19,11 @@ if s1.val["type"] and s1.val["type"] ~= type_name then
return
end
local s = NamedSection(m, arg[1], "server")
local s = NamedSection(m, arg[1], "tmp_" .. s1.sectiontype)
s.parent = s1
s.type_name = type_name
s.option_prefix = "xray_"
api.set_type_cbi(s)
local ss_method_list = {
"aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"
@@ -44,13 +46,13 @@ o.validate = function(self, value)
if v then return v end
return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!")
end
o.custom_cfgvalue = function(self, section, value)
o.cfgvalue = function(self, section, value)
local config_str = m:get(section, "config_str")
if config_str then
return api.base64Decode(config_str)
end
end
o.custom_write = function(self, section, value)
o.write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "")
end
@@ -204,13 +206,18 @@ o.validate = function(self, value, t)
end
o:depends({ protocol = "vmess" })
o:depends({ protocol = "vless" })
o:depends({ protocol = "http" })
o:depends({ protocol = "shadowsocks" })
o:depends({ protocol = "trojan" })
-- [[ REALITY部分 ]] --
o = s:option(Flag, "reality", translate("REALITY"))
o.default = 0
o:depends({ tls = true })
o:depends({ tls = true, transport = "raw" })
o:depends({ tls = true, transport = "ws" })
o:depends({ tls = true, transport = "grpc" })
o:depends({ tls = true, transport = "httpupgrade" })
o:depends({ tls = true, transport = "xhttp" })
o = s:option(Value, "reality_private_key", translate("Private Key"))
o:depends({ reality = true })
@@ -257,8 +264,8 @@ end
-- [[ TLS部分 ]] --
o = s:option(FileUpload, "tls_certificateFile", translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. s.section .. ".pem"
if o and o:formvalue(s.section) then o.default = o:formvalue(s.section) end
o:depends({ tls = true, reality = false })
o:depends({ protocol = "hysteria2"})
o.validate = function(self, value, t)
@@ -273,8 +280,8 @@ o.validate = function(self, value, t)
end
o = s:option(FileUpload, "tls_keyFile", translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key")
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end
o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. s.section .. ".key"
if o and o:formvalue(s.section) then o.default = o:formvalue(s.section) end
o:depends({ tls = true, reality = false })
o:depends({ protocol = "hysteria2"})
o.validate = function(self, value, t)
@@ -396,13 +403,13 @@ o:depends({ use_finalmask = true })
o.rows = 10
o.wrap = "off"
o.datatype = "json"
o.custom_cfgvalue = function(self, section, value)
o.cfgvalue = function(self, section, value)
local raw = m:get(section, "finalmask")
if raw then
return api.base64Decode(raw)
end
end
o.custom_write = function(self, section, value)
o.write = function(self, section, value)
m:set(section, "finalmask", api.base64Encode(value) or "")
end
@@ -549,4 +556,4 @@ o:value("warning")
o:value("error")
o:depends({ log = true })
api.luci_types(s1, s)
api.type_cbi_section(s1, s)
@@ -16,9 +16,11 @@ if s1.val["type"] and s1.val["type"] ~= type_name then
return
end
local s = NamedSection(m, arg[1], "server")
local s = NamedSection(m, arg[1], "tmp_" .. s1.sectiontype)
s.parent = s1
s.type_name = type_name
s.option_prefix = "ssrust_"
api.set_type_cbi(s)
local ssrust_encrypt_method_list = {
"plain", "none",
@@ -39,13 +41,13 @@ o.validate = function(self, value)
if v then return v end
return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!")
end
o.custom_cfgvalue = function(self, section, value)
o.cfgvalue = function(self, section, value)
local config_str = m:get(section, "config_str")
if config_str then
return api.base64Decode(config_str)
end
end
o.custom_write = function(self, section, value)
o.write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "")
end
@@ -88,4 +90,4 @@ o = s:option(Flag, "log", translate("Log"))
o.default = "1"
o.rmempty = false
api.luci_types(s1, s)
api.type_cbi_section(s1, s)
@@ -16,9 +16,11 @@ if s1.val["type"] and s1.val["type"] ~= type_name then
return
end
local s = NamedSection(m, arg[1], "server")
local s = NamedSection(m, arg[1], "tmp_" .. s1.sectiontype)
s.parent = s1
s.type_name = type_name
s.option_prefix = "ssr_"
api.set_type_cbi(s)
local ssr_encrypt_method_list = {
"none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb",
@@ -52,13 +54,13 @@ o.validate = function(self, value)
if v then return v end
return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!")
end
o.custom_cfgvalue = function(self, section, value)
o.cfgvalue = function(self, section, value)
local config_str = m:get(section, "config_str")
if config_str then
return api.base64Decode(config_str)
end
end
o.custom_write = function(self, section, value)
o.write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "")
end
@@ -119,4 +121,4 @@ o = s:option(Flag, "log", translate("Log"))
o.default = "1"
o.rmempty = false
api.luci_types(s1, s)
api.type_cbi_section(s1, s)
@@ -12,13 +12,11 @@ if s1.val["type"] and s1.val["type"] ~= type_name then
return
end
local s = NamedSection(m, arg[1], "server")
local s = NamedSection(m, arg[1], "tmp_" .. s1.sectiontype)
s.parent = s1
s.type_name = type_name
s.option_prefix = "hysteria2_"
local function _n(name)
return s.option_prefix .. name
end
api.set_type_cbi(s)
o = s:option(Flag, "custom", translate("Use Custom Config"))
@@ -33,13 +31,13 @@ o.validate = function(self, value)
if v then return v end
return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!")
end
o.custom_cfgvalue = function(self, section, value)
o.cfgvalue = function(self, section, value)
local config_str = m:get(section, "config_str")
if config_str then
return api.base64Decode(config_str)
end
end
o.custom_write = function(self, section, value)
o.write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "")
end
@@ -55,11 +53,11 @@ o:depends({ custom = false })
o = s:option(Flag, "realms", translate("Realms"))
o.default = "0"
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o:depends({ custom = false })
o = s:option(Value, "realm_url", translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o:depends({ realms = "1" })
o.validate = function(self, value)
value = api.trim(value)
@@ -70,23 +68,23 @@ end
o = s:option(DynamicList, "realm_stun", translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o:depends({ realms = "1" })
o = s:option(Flag, "realm_upnp", translate("Enable") .. " UPnP/NAT-PMP", translate("Enable UPnP/NAT-PMP port mapping on your gateway to improve hole punching success."))
o.default = "0"
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o:depends({ realms = "1" })
o = s:option(ListValue, "obfs_type", translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:value("gecko")
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o:depends({ custom = false })
o = s:option(Value, "obfs_password", translate("Obfs Password"))
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o:depends({ obfs_type = "salamander" })
o:depends({ obfs_type = "gecko" })
@@ -95,31 +93,31 @@ o.datatype = "uinteger"
o.placeholder = "512"
o.default = "512"
o:depends({ obfs_type = "gecko" })
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o = s:option(Value, "obfs_MaxPacketSize", translate("Gecko Packet Size (max)"))
o.datatype = "uinteger"
o.placeholder = "1200"
o.default = "1200"
o:depends({ obfs_type = "gecko" })
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o = s:option(Flag, "udp", translate("UDP"))
o.default = "1"
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o:depends({ custom = false })
o = s:option(Value, "up_mbps", translate("Max upload Mbps"))
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o:depends({ custom = false })
o = s:option(Value, "down_mbps", translate("Max download Mbps"))
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o:depends({ custom = false })
o = s:option(Flag, "ignoreClientBandwidth", translate("ignoreClientBandwidth"))
o.default = "0"
o.rewrite_option = _n(o.option)
o.rewrite_option = o.option
o:depends({ custom = false })
o = s:option(FileUpload, "tls_certificateFile", translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem")
@@ -181,4 +179,4 @@ o = s:option(Flag, "log", translate("Log"))
o.default = "1"
o.rmempty = false
api.luci_types(s1, s)
api.type_cbi_section(s1, s)
+130 -114
View File
@@ -163,12 +163,16 @@ function sh_uci_commit(config)
exec_call(string.format("uci -q commit %s", config))
end
function del_cache_var(key)
sys.call(string.format('. /usr/share/passwall2/utils.sh ; del_cache_var "%s"', key))
end
function set_cache_var(key, val)
sys.call(string.format('. /usr/share/passwall/utils.sh ; set_cache_var %s "%s"', key, val))
sys.call(string.format('. /usr/share/passwall/utils.sh ; set_cache_var "%s" "%s"', key, val))
end
function get_cache_var(key)
local val = sys.exec(string.format('. /usr/share/passwall/utils.sh ; echo -n $(get_cache_var %s)', key))
local val = sys.exec(string.format('. /usr/share/passwall/utils.sh ; echo -n $(get_cache_var "%s")', key))
if val == "" then val = nil end
return val
end
@@ -1378,11 +1382,22 @@ function to_move(app_name,file)
end
function get_version()
local version = sys.exec("opkg list-installed luci-app-passwall 2>/dev/null | awk '{print $3}'")
if not version or #version == 0 then
version = sys.exec("apk list luci-app-passwall 2>/dev/null | awk '/installed/ {print $1}' | cut -d'-' -f4-")
local version
local version_file = CACHE_PATH .. "/passwall_version"
sys.call("mkdir -p " .. CACHE_PATH)
if fs.access(version_file) then
version = fs.readfile(version_file)
else
version = sys.exec("opkg list-installed luci-app-passwall 2>/dev/null | awk '{print $3}'")
if not version or version == "" then
version = sys.exec("apk list luci-app-passwall 2>/dev/null | awk '/installed/ {print $1}' | cut -d'-' -f4-")
end
version = (version or ""):match("^%s*(.-)%s*$")
if version ~= "" then
fs.writefile(version_file, version)
end
end
return (version or ""):gsub("\n", ""):match("^([^-]+)")
return version:match("^([^-]+)") or ""
end
function to_check_self()
@@ -1506,11 +1521,111 @@ function set_default_cbi()
return cbi.AbstractValue.write(self, section, new_val)
end
end
if true then
--HideValue
local HideValue = util.class(cbi.DummyValue)
function HideValue.__init__(self, ...)
cbi.DummyValue.__init__(self, ...)
self.template = self.map:template_path("/cbi/hidevalue")
self.value = "1"
end
cbi.HideValue = HideValue
end
end
function set_type_cbi(s)
local cbi = require "luci.cbi"
local s1 = s.parent
function s.option(s_self, class, option, ...)
local obj = class(s_self.map, s_self, option, ...)
obj.config_option = option
obj.option_prefix = s_self.option_prefix
obj.option = s_self.option_prefix .. option
obj.cfgvalue = function(self, section)
if self.rewrite_option then
return self.map:get(section, self.rewrite_option)
else
return self.map:get(section, self.config_option)
end
end
obj.write = function(self, section, value)
if s1.fields["type"]:formvalue(s_self.section) == s_self.type_name then
local new_val = value
if util.instanceof(self, cbi.DynamicList) then
local new_t = {}
if type(value) == "table" then
new_t = table_remove_duplicates(value)
else
new_t = { value }
end
if self.cast == "string" then
new_val = table.concat(new_t, " ")
else
new_val = new_t
end
end
if self.rewrite_option then
self.map:set(section, self.rewrite_option, new_val)
else
self.map:set(section, self.config_option, new_val)
end
end
end
obj.remove = function(self, section)
if s1.fields["type"]:formvalue(s_self.section) == s_self.type_name then
if self.rewrite_option then
self.map:del(section, self.rewrite_option)
else
self.map:del(section, self.config_option)
end
end
end
obj.deplist2json = function(self, section, deplist)
local deps, i, d = { }
if type(self.deps) == "table" then
if not next(self.deps) then
self:depends({ type = s.type_name })
end
local list = deplist or self.deps
for i, d in ipairs(list) do
if s.type_name and not d["type"] then
d["type"] = s.type_name
end
local a, k, v = { }
for k, v in pairs(d) do
if k:find("!", 1, true) then
a[k] = v
elseif k:find("^", 1, true) then
a[k:sub(2)] = v
elseif k:find(".", 1, true) then
a['cbid%s' % k] = v
elseif s_self.fields[k] then
a['cbid.%s.%s.%s' %{ self.config, section, s_self.fields[k].option }] = v
else
a['cbid.%s.%s.%s' %{ self.config, section, k }] = v
end
end
deps[#deps+1] = a
end
end
return util.serialize_json(deps)
end
s_self:append(obj)
s_self.fields[option] = obj
return obj
end
end
function type_cbi_section(s, s2)
for i, v in ipairs(s2.children) do
local o = s2.children[i]
s:append(o)
s.fields[o.option] = o
end
end
function return_map(map)
local cbi = require "luci.cbi"
local api = require "luci.passwall.api"
if true then
-- header
local header = cbi.Template(appname .. "/cbi/header")
@@ -1526,113 +1641,6 @@ function return_map(map)
return map
end
function luci_types(s, s2)
local cbi = require "luci.cbi"
local m = s.map
local id = s2.section
local type_name = s2.type_name
local option_prefix = s2.option_prefix
local fv_type
local field_type = s.fields["type"]
if field_type then
fv_type = field_type:formvalue(id)
end
for i, v in ipairs(s2.children) do
local o = s2.children[i]
o.config_option = o.option
o.option_prefix = option_prefix
o.option = option_prefix .. o.option
if not o.not_rewrite then
o.cfgvalue = function(self, section)
-- Add a custom `custom_cfgvalue` attribute. If a custom `custom_cfgvalue` function exists, the custom `cfgvalue` logic will be used.
if self.custom_cfgvalue then
return self:custom_cfgvalue(section)
else
if self.rewrite_option then
return m:get(section, self.rewrite_option)
else
return m:get(section, self.config_option)
end
end
end
o.write = function(self, section, value)
if s.fields["type"]:formvalue(id) == type_name then
-- Add a custom `custom_write` attribute; if a custom `custom_write` function exists, then use the custom write logic.
if self.custom_write then
self:custom_write(section, value)
else
local new_val = value
if util.instanceof(self, cbi.DynamicList) then
local new_t = {}
if type(value) == "table" then
new_t = table_remove_duplicates(value)
else
new_t = { value }
end
if self.cast == "string" then
new_val = table.concat(new_t, " ")
else
new_val = new_t
end
end
if self.rewrite_option then
m:set(section, self.rewrite_option, new_val)
else
m:set(section, self.config_option, new_val)
end
end
end
end
o.remove = function(self, section)
if s.fields["type"]:formvalue(id) == type_name then
-- Add a custom `custom_remove` attribute; if a custom `custom_remove` function exists, use the custom remove logic.
if self.custom_remove then
self:custom_remove(section)
else
if self.rewrite_option then
m:del(section, self.rewrite_option)
else
m:del(section, self.config_option)
end
end
end
end
end
local deps = o.deps
if #deps > 0 then
local function process_deps(dep)
local rewrite_deps = {}
for k, v in pairs(dep) do
if k:find("!") then
rewrite_deps[k] = v
else
rewrite_deps[option_prefix .. k] = v
end
end
if not rewrite_deps['!reverse'] then
rewrite_deps["type"] = type_name
end
return rewrite_deps
end
for index, value in ipairs(deps) do
local rewrite_deps = process_deps(value)
if rewrite_deps then
deps[index] = rewrite_deps
end
end
else
o:depends({ type = type_name })
end
if fv_type and fv_type ~= type_name then
o.rmempty = true
end
s:append(o)
end
end
function get_std_domain(domain)
domain = trim(domain)
if domain == "" then return "" end
@@ -2068,3 +2076,11 @@ function gen_wireguard_key()
}
end
end
function get_socks_port_by_cache(node_id)
return get_cache_var("node_%s_socks_port" % { node_id })
end
function set_socks_port_to_cache(node_id, v)
set_cache_var("node_%s_socks_port" % { node_id }, v)
end
@@ -93,43 +93,64 @@ function gen_outbound(flag, node, tag, proxy_table)
end
local remarks = node.remarks
local proxy_tag = nil
local fragment = nil
local record_fragment = nil
local run_socks_instance = true
local proxy_tag, fragment, record_fragment
if proxy_table ~= nil and type(proxy_table) == "table" then
proxy_tag = proxy_table.tag or nil
fragment = (proxy_table.fragment and node.protocol ~= "naive" and not node.hysteria2_realms) and true or nil
record_fragment = (proxy_table.record_fragment and node.protocol ~= "naive" and not node.hysteria2_realms) and true or nil
run_socks_instance = proxy_table.run_socks_instance
end
if node.type ~= "sing-box" then
local relay_port = node.port
local new_port = api.get_new_port()
local config_file = string.format("%s_%s_%s.json", flag, tag, new_port)
if tag and node_id and not tag:find(node_id) then
config_file = string.format("%s_%s_%s_%s.json", flag, tag, node_id, new_port)
end
if run_socks_instance then
sys.call(string.format('/usr/share/passwall/app.sh run_socks "%s"> /dev/null',
string.format("flag=%s node=%s bind=%s socks_port=%s config_file=%s relay_port=%s",
new_port, --flag
node_id, --node
"127.0.0.1", --bind
new_port, --socks port
config_file, --config file
(proxy_tag and relay_port) and tostring(relay_port) or "" --relay port
if node.type == "Socks" then
node.protocol = "socks"
proxy_tag = "socks <- " .. node_id
else
local new_port
local run_socks_instance = true
if NO_RUN then
TMP_PORT = TMP_PORT and TMP_PORT + 1 or 3001
new_port = TMP_PORT
run_socks_instance = nil
else
local relay_port = (proxy_tag and node.port) and tostring(node.port) or ""
if relay_port == "" then
local cache = api.get_socks_port_by_cache(node_id)
if cache then
new_port = cache
run_socks_instance = nil
end
end
if run_socks_instance then
new_port = api.get_new_port()
local config_file = string.format("nodesocks_%s_%s.json", node_id, new_port)
if tag and node_id and not tag:find(node_id) then
config_file = string.format("nodesocks_%s_%s_%s.json", tag, node_id, new_port)
end
sys.call(string.format('/usr/share/passwall/app.sh run_socks "%s"> /dev/null',
string.format("flag=%s node=%s bind=%s socks_port=%s config_file=%s relay_port=%s",
new_port, --flag
node_id, --node
"127.0.0.1", --bind
new_port, --socks port
config_file, --config file
relay_port --relay port
)
)
)
)
)
if relay_port == "" then
api.set_socks_port_to_cache(node_id, new_port)
end
end
end
if new_port then
node = {
protocol = "socks",
address = "127.0.0.1",
port = new_port
}
proxy_tag = "socks <- " .. node_id
end
end
node = {
protocol = "socks",
address = "127.0.0.1",
port = new_port
}
proxy_tag = "socks <- " .. node_id
else
if proxy_tag then
node.detour = proxy_tag
@@ -481,9 +502,9 @@ function gen_outbound(flag, node, tag, proxy_table)
obfs = node.hysteria_obfs,
auth = (node.hysteria_auth_type == "base64") and node.hysteria_auth_password or nil,
auth_str = (node.hysteria_auth_type == "string") and node.hysteria_auth_password or nil,
recv_window_conn = tonumber(node.hysteria_recv_window_conn), --1.14 将变更为 stream_receive_window
recv_window = tonumber(node.hysteria_recv_window), --1.14 将变更为 connection_receive_window
disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false, --1.14 将变更为 disable_path_mtu_discovery
stream_receive_window = tonumber(node.hysteria_recv_window_conn),
connection_receive_window = tonumber(node.hysteria_recv_window),
disable_path_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false,
tls = tls
}
end
@@ -910,10 +931,10 @@ function gen_config_server(node)
down_mbps = tonumber(node.hysteria_down_mbps),
obfs = node.hysteria_obfs,
users = users,
recv_window_conn = node.hysteria_recv_window_conn and tonumber(node.hysteria_recv_window_conn) or nil, --1.14 to stream_receive_window
recv_window_client = node.hysteria_recv_window_client and tonumber(node.hysteria_recv_window_client) or nil, --1.14 to connection_receive_window
max_conn_client = node.hysteria_max_conn_client and tonumber(node.hysteria_max_conn_client) or nil, --1.14 to max_concurrent_streams
disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false, --1.14 to disable_path_mtu_discover
stream_receive_window = node.hysteria_recv_window_conn and tonumber(node.hysteria_recv_window_conn) or nil,
connection_receive_window = node.hysteria_recv_window_client and tonumber(node.hysteria_recv_window_client) or nil,
max_concurrent_streams = node.hysteria_max_conn_client and tonumber(node.hysteria_max_conn_client) or nil,
disable_path_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false,
tls = tls
}
end
@@ -1140,10 +1161,11 @@ function gen_config(var)
local dns_cache = var["dns_cache"]
local dns_socks_address = var["dns_socks_address"]
local dns_socks_port = var["dns_socks_port"]
local no_run = var["no_run"]
local use_proxy_list = var["use_proxy_list"]
local use_gfw_list = var["use_gfw_list"]
local chn_list = var["chn_list"]
local run_in_global = var["run_in_global"]
NO_RUN = var["no_run"]
local dns_domain_rules = {}
local dns = nil
@@ -1337,7 +1359,9 @@ function gen_config(var)
ut_nodes = _node.urltest_node
end
-- api.log(" - 加载 Sing-Box URLTest 节点【" .. (_node.remarks or "") .. "】,子节点数量:" .. #(ut_nodes or {}))
if not NO_RUN and run_in_global then
api.log(" - 加载 Sing-Box URLTest 节点【" .. (_node.remarks or "") .. "】,子节点数量:" .. #(ut_nodes or {}))
end
local valid_nodes = {}
for i = 1, #(ut_nodes or {}) do
@@ -1352,7 +1376,7 @@ function gen_config(var)
end
end
if is_new_ut_node then
local outboundTag = gen_outbound_get_tag(flag, ut_node_id, ut_node_tag, { fragment = singbox_settings.fragment == "1" or nil, record_fragment = singbox_settings.record_fragment == "1" or nil, run_socks_instance = not no_run })
local outboundTag = gen_outbound_get_tag(flag, ut_node_id, ut_node_tag, { fragment = singbox_settings.fragment == "1" or nil, record_fragment = singbox_settings.record_fragment == "1" or nil })
if outboundTag then
valid_nodes[#valid_nodes + 1] = outboundTag
end
@@ -1455,7 +1479,6 @@ function gen_config(var)
to_node.port = new_port
to_outbound = gen_outbound(node[".name"], to_node, tag, {
tag = tag,
run_socks_instance = not no_run
})
else
to_outbound = gen_outbound(node[".name"], to_node)
@@ -1548,7 +1571,6 @@ function gen_config(var)
local proxy_table = {
fragment = singbox_settings.fragment == "1",
record_fragment = singbox_settings.record_fragment == "1",
run_socks_instance = not no_run,
}
local preproxy_node_id = node[rule_name .. "_proxy_tag"]
if preproxy_node_id == _node_id then preproxy_node_id = nil end
@@ -1832,7 +1854,6 @@ function gen_config(var)
COMMON.default_outbound_tag = gen_outbound_get_tag(flag, node or node_id, nil, {
fragment = singbox_settings.fragment == "1" or nil,
record_fragment = singbox_settings.record_fragment == "1" or nil,
run_socks_instance = not no_run
})
end
@@ -2216,14 +2237,10 @@ function gen_config(var)
})
else
table.insert(dns.rules, 1, {
action = "evaluate",
preferred_by = "hosts",
action = "route",
server = "hosts"
})
table.insert(dns.rules, 2, {
match_response = true,
ip_accept_any = true,
action = "respond"
})
end
if COMMON.default_outbound_tag == "block" then
@@ -2297,7 +2314,7 @@ function gen_config(var)
routing_mark = 255,
})
for index, value in ipairs(config.outbounds) do
if not value["_flag_proxy_tag"] and not value.detour and value["_id"] and value.server and (value.server_port or value.server_ports) and not no_run then
if not value["_flag_proxy_tag"] and not value.detour and value["_id"] and value.server and (value.server_port or value.server_ports) and not NO_RUN then
sys.call(string.format("echo '%s' >> %s", value["_id"], api.TMP_PATH .. "/direct_node_list"))
end
if not value.detour and not value.bind_interface and value.server then
@@ -2445,7 +2462,7 @@ if arg[1] then
var = jsonc.parse(arg[2])
end
print(func(var))
if (next(GEO_VAR.SITE_TAGS) or next(GEO_VAR.IP_TAGS)) and not no_run then
if (next(GEO_VAR.SITE_TAGS) or next(GEO_VAR.IP_TAGS)) and not NO_RUN then
convert_geofile()
end
end
+91 -76
View File
@@ -12,7 +12,7 @@ local GLOBAL = {
local xray_version = api.get_app_version("xray")
local xray_min_version = "26.3.27"
local xray_min_version = "26.7.11"
local function get_domain_excluded()
local path = string.format("/usr/share/%s/rules/domains_excluded", api.c_config)
@@ -43,55 +43,67 @@ function gen_outbound(flag, node, tag, proxy_table)
end
local remarks = node.remarks
local proxy_tag = nil
local fragment = nil
local noise = nil
local run_socks_instance = true
local proxy_tag, dialer_proxy_tag, fragment, noise
if proxy_table ~= nil and type(proxy_table) == "table" then
proxy_tag = proxy_table.tag or nil
fragment = (proxy_table.fragment and not node.hysteria2_realms) and true or nil
noise = (proxy_table.noise and not node.hysteria2_realms) and true or nil
run_socks_instance = proxy_table.run_socks_instance
end
if node.type ~= "Xray" then
if node.type == "Socks" then
node.protocol = "socks"
node.transport = "tcp"
node.transport = "raw"
else
local relay_port = node.port
local new_port = api.get_new_port()
local config_file = string.format("%s_%s_%s.json", flag, tag, new_port)
if tag and node_id and not tag:find(node_id) then
config_file = string.format("%s_%s_%s_%s.json", flag, tag, node_id, new_port)
end
if run_socks_instance then
sys.call(string.format('/usr/share/passwall/app.sh run_socks "%s"> /dev/null',
string.format("flag=%s node=%s bind=%s socks_port=%s config_file=%s relay_port=%s",
new_port, --flag
node_id, --node
"127.0.0.1", --bind
new_port, --socks port
config_file, --config file
(proxy_tag and relay_port) and tostring(relay_port) or "" --relay port
local new_port
local run_socks_instance = true
if NO_RUN then
TMP_PORT = TMP_PORT and TMP_PORT + 1 or 3001
new_port = TMP_PORT
run_socks_instance = nil
else
local relay_port = (proxy_tag and node.port) and tostring(node.port) or ""
if relay_port == "" then
local cache = api.get_socks_port_by_cache(node_id)
if cache then
new_port = cache
run_socks_instance = nil
end
end
if run_socks_instance then
new_port = api.get_new_port()
local config_file = string.format("nodesocks_%s_%s.json", node_id, new_port)
if tag and node_id and not tag:find(node_id) then
config_file = string.format("nodesocks_%s_%s_%s.json", tag, node_id, new_port)
end
sys.call(string.format('/usr/share/passwall/app.sh run_socks "%s"> /dev/null',
string.format("flag=%s node=%s bind=%s socks_port=%s config_file=%s relay_port=%s",
new_port, --flag
node_id, --node
"127.0.0.1", --bind
new_port, --socks port
config_file, --config file
relay_port --relay port
)
)
)
))
if relay_port == "" then
api.set_socks_port_to_cache(node_id, new_port)
end
end
end
if new_port then
node = {}
node.protocol = "socks"
node.transport = "raw"
node.address = "127.0.0.1"
node.port = new_port
end
node = {}
node.protocol = "socks"
node.transport = "tcp"
node.address = "127.0.0.1"
node.port = new_port
end
node.stream_security = "none"
proxy_tag = "socks <- " .. node_id
else
if proxy_tag then
node.proxySettings = {
tag = proxy_tag,
transportLayer = true
}
end
dialer_proxy_tag = proxy_tag
end
if node.type == "Xray" then
@@ -124,6 +136,10 @@ function gen_outbound(flag, node, tag, proxy_table)
node.stream_security = "tls"
end
if node.protocol == "http" and node.stream_security == "tls" then
node.transport = "raw"
end
if remarks then
tag = tag .. ":" .. remarks
end
@@ -135,7 +151,6 @@ function gen_outbound(flag, node, tag, proxy_table)
_flag = flag,
_flag_proxy_tag = proxy_tag,
tag = tag,
proxySettings = node.proxySettings or nil,
protocol = node.protocol,
mux = {
enabled = (node.mux == "1") and true or false,
@@ -143,7 +158,7 @@ function gen_outbound(flag, node, tag, proxy_table)
xudpConcurrency = (node.mux == "1" and ((node.xudp_concurrency) and tonumber(node.xudp_concurrency) or 8)) or nil
} or nil,
-- 底层传输配置
streamSettings = (node.streamSettings or node.protocol == "vmess" or node.protocol == "vless" or node.protocol == "socks" or node.protocol == "shadowsocks" or node.protocol == "trojan" or node.protocol == "hysteria") and {
streamSettings = (node.streamSettings or dialer_proxy_tag or node.protocol == "vmess" or node.protocol == "vless" or node.protocol == "socks" or node.protocol == "shadowsocks" or node.protocol == "trojan" or node.protocol == "hysteria" or node.protocol == "http") and {
sockopt = {
mark = 255,
domainStrategy = node.domain_strategy or "UseIP",
@@ -154,9 +169,10 @@ function gen_outbound(flag, node, tag, proxy_table)
PrioritizeIPv6 = false,
Interleave = 1,
MaxConcurrentTry = 4
} or nil
} or nil,
dialerProxy = dialer_proxy_tag,
},
[(api.compare_versions(xray_version, "<", "26.7.11")) and "network" or "method"] = node.transport, -- Todo: Remove version check and "network"
method = node.transport,
security = node.stream_security,
tlsSettings = (node.stream_security == "tls") and {
serverName = node.tls_serverName,
@@ -548,7 +564,7 @@ function gen_config_server(node)
allowTransparent = false,
users = users
}
node.transport = "tcp"
node.transport = "raw"
node.tcp_guise = "none"
elseif node.protocol == "shadowsocks" then
settings = {
@@ -675,7 +691,7 @@ function gen_config_server(node)
protocol = node.protocol,
settings = settings,
streamSettings = {
[(api.compare_versions(xray_version, "<", "26.7.11")) and "network" or "method"] = node.transport, -- Todo: Remove version check and "network"
method = node.transport,
security = "none",
tlsSettings = ("1" == node.tls) and {
disableSystemRoot = false,
@@ -857,9 +873,6 @@ function gen_config_server(node)
config.outbounds[index][k] = nil
end
end
if value.protocol == "freedom" and api.compare_versions(xray_version, "<", "26.5.3") then -- Todo is to remove it
value.settings = nil
end
end
return config
@@ -897,10 +910,11 @@ function gen_config(var)
local dns_socks_address = var["dns_socks_address"]
local dns_socks_port = var["dns_socks_port"]
local loglevel = var["loglevel"] or "warning"
local no_run = var["no_run"]
local use_proxy_list = var["use_proxy_list"]
local use_gfw_list = var["use_gfw_list"]
local chn_list = var["chn_list"]
local run_in_global = var["run_in_global"]
NO_RUN = var["no_run"]
local dns_domain_rules = {}
local dns = nil
@@ -1075,7 +1089,9 @@ function gen_config(var)
blc_nodes = _node.balancing_node
end
-- api.log(" - 加载 Xray 负载均衡 节点【" .. (_node.remarks or "") .. "】,子节点数量:" .. #(blc_nodes or {}))
if not NO_RUN and run_in_global then
api.log(" - 加载 Xray 负载均衡 节点【" .. (_node.remarks or "") .. "】,子节点数量:" .. #(blc_nodes or {}))
end
local valid_nodes = {}
for i = 1, #(blc_nodes or {}) do
@@ -1090,7 +1106,7 @@ function gen_config(var)
end
end
if is_new_blc_node then
local outboundTag = gen_outbound_get_tag(flag, blc_node_id, blc_node_tag, { fragment = xray_settings.fragment == "1" or nil, noise = xray_settings.noise == "1" or nil, run_socks_instance = not no_run })
local outboundTag = gen_outbound_get_tag(flag, blc_node_id, blc_node_tag, { fragment = xray_settings.fragment == "1" or nil, noise = xray_settings.noise == "1" or nil })
if outboundTag then
valid_nodes[#valid_nodes + 1] = outboundTag
end
@@ -1119,7 +1135,7 @@ function gen_config(var)
local fallback_node = get_node_by_id(fallback_node_id)
if fallback_node then
if fallback_node.protocol ~= "_balancing" then
local outboundTag = gen_outbound_get_tag(flag, fallback_node, fallback_node_id, { fragment = xray_settings.fragment == "1" or nil, noise = xray_settings.noise == "1" or nil, run_socks_instance = not no_run })
local outboundTag = gen_outbound_get_tag(flag, fallback_node, fallback_node_id, { fragment = xray_settings.fragment == "1" or nil, noise = xray_settings.noise == "1" or nil })
if outboundTag then
fallback_node_tag = outboundTag
end
@@ -1209,10 +1225,9 @@ function gen_config(var)
end
if preproxy_outbound then
outbound.tag = preproxy_outbound.tag .. " -> " .. outbound.tag
outbound.proxySettings = {
tag = preproxy_outbound.tag,
transportLayer = true
}
outbound.streamSettings = outbound.streamSettings or {}
outbound.streamSettings.sockopt = outbound.streamSettings.sockopt or {}
outbound.streamSettings.sockopt.dialerProxy = preproxy_outbound.tag
if not exist then
last_insert_outbound = preproxy_outbound
end
@@ -1252,7 +1267,6 @@ function gen_config(var)
})
to_outbound = gen_outbound(node[".name"], to_node, to_node[".name"], {
tag = to_node[".name"],
run_socks_instance = not no_run
})
else
to_outbound = gen_outbound(node[".name"], to_node)
@@ -1260,10 +1274,9 @@ function gen_config(var)
if to_outbound then
to_outbound.tag = outbound.tag .. " -> " .. to_outbound.tag
if to_node.type == "Xray" then
to_outbound.proxySettings = {
tag = outbound.tag,
transportLayer = true
}
to_outbound.streamSettings = to_outbound.streamSettings or {}
to_outbound.streamSettings.sockopt = to_outbound.streamSettings.sockopt or {}
to_outbound.streamSettings.sockopt.dialerProxy = outbound.tag
end
table.insert(outbounds_table, to_outbound)
default_outTag = to_outbound.tag
@@ -1318,9 +1331,9 @@ function gen_config(var)
interface = node.iface
}
},
settings = (api.compare_versions(xray_version, ">", "26.4.25")) and { -- Todo: Remove version check
settings = {
finalRules = {{ action = "allow" }}
} or nil
}
}
sys.call(string.format("mkdir -p %s && touch %s/%s", api.TMP_IFACE_PATH, api.TMP_IFACE_PATH, node.iface))
end
@@ -1361,7 +1374,6 @@ function gen_config(var)
local proxy_table = {
fragment = xray_settings.fragment == "1",
noise = xray_settings.noise == "1",
run_socks_instance = not no_run,
}
local preproxy_node_id = node[rule_name .. "_proxy_tag"]
if preproxy_node_id == _node_id then preproxy_node_id = nil end
@@ -1563,7 +1575,6 @@ function gen_config(var)
COMMON.default_outbound_tag = gen_outbound_get_tag(flag, node or node_id, nil, {
fragment = xray_settings.fragment == "1" or nil,
noise = xray_settings.noise == "1" or nil,
run_socks_instance = not no_run
})
if COMMON.default_outbound_tag then
routing = {
@@ -1800,15 +1811,14 @@ function gen_config(var)
remote_dns_outbound = {
tag = "dns-out",
protocol = "dns",
proxySettings = dns_outbound_tag and {
tag = (dns_outbound_tag ~= "blackhole") and dns_outbound_tag or "direct"
streamSettings = dns_outbound_tag and {
sockopt = { dialerProxy = (dns_outbound_tag ~= "blackhole") and dns_outbound_tag or "direct" }
} or nil,
settings = {
address = (chn_list ~= "proxy") and "8.8.8.8" or "223.5.5.5",
port = 53,
network = "tcp",
nonIPQuery = (api.compare_versions(xray_version, "<", "26.4.25")) and "reject" or nil, -- Todo is to remove it
rules = (api.compare_versions(xray_version, ">", "26.4.17")) and {} or nil
rewriteAddress = (chn_list ~= "proxy") and "8.8.8.8" or "223.5.5.5",
rewritePort = 53,
rewriteNetwork = "tcp",
rules = {}
}
}
@@ -2033,12 +2043,12 @@ function gen_config(var)
protocol = "freedom",
tag = "direct",
settings = {
domainStrategy = (direct_dns_query_strategy and direct_dns_query_strategy ~= "") and direct_dns_query_strategy or "UseIP",
finalRules = (api.compare_versions(xray_version, ">", "26.4.25")) and {{ action = "allow" }} or nil -- Todo: Remove version check
finalRules = {{ action = "allow" }}
},
streamSettings = {
sockopt = {
mark = 255
mark = 255,
domainStrategy = (direct_dns_query_strategy and direct_dns_query_strategy ~= "") and direct_dns_query_strategy or "UseIP"
}
}
}
@@ -2060,7 +2070,7 @@ function gen_config(var)
for index, value in ipairs(config.outbounds) do
local pt = value.protocol
local exclude = { blackhole=1, dns=1, freedom=1, loopback=1 }
if not value["_flag_proxy_tag"] and value["_id"] and pt and not exclude[pt] and not no_run then
if not value["_flag_proxy_tag"] and value["_id"] and pt and not exclude[pt] and not NO_RUN then
sys.call(string.format("echo '%s' >> %s", value["_id"], api.TMP_PATH .. "/direct_node_list"))
end
for k, v in pairs(config.outbounds[index]) do
@@ -2138,7 +2148,7 @@ function gen_proto_config(var)
local outbound = {
protocol = server_proto,
streamSettings = {
network = "tcp",
method = "raw",
security = "none"
},
settings = {
@@ -2163,10 +2173,12 @@ function gen_proto_config(var)
table.insert(outbounds, {
protocol = "freedom",
tag = "direct",
settings = (api.compare_versions(xray_version, ">", "26.4.25")) and { -- Todo: Remove version check
settings = {
finalRules = {{ action = "allow" }}
} or nil,
sockopt = {mark = 255}
},
streamSettings = {
sockopt = {mark = 255}
}
})
local config = {
@@ -2178,7 +2190,10 @@ function gen_proto_config(var)
-- 传出连接
outbounds = outbounds,
-- 路由
routing = routing
routing = routing,
version = {
min = xray_min_version
}
}
return jsonc.stringify(config, 1)
end
@@ -1,121 +0,0 @@
<%
local map = self.map
local api = map.api
local sid = self.section
-%>
<script type="text/javascript">
//<![CDATA[
function to_edit_node(btn) {
if (!btn) return;
const idReg = /^cbid\..*\..*node$/;
let hidden_select = null;
const selects = document.querySelectorAll('select[id^="cbid."][id*="."][id$="node"]');
for (const sel of selects) {
if ( idReg.test(sel.id) && getComputedStyle(sel).display === "none" && (sel.compareDocumentPosition(btn) & Node.DOCUMENT_POSITION_FOLLOWING)) {
hidden_select = sel;
}
}
if (!hidden_select) return;
let node_select_value = hidden_select?.options[0]?.value;
if (!node_select_value || node_select_value.indexOf("tcp") === 0) {
return;
}
let to_url = '<%=api.url("node_config")%>/' + node_select_value;
if (node_select_value.indexOf("socks_") === 0) {
to_url = '<%=api.url("socks_config")%>/' + node_select_value;
}
location.href = to_url;
}
document.addEventListener('DOMContentLoaded', function () {
function go() {
const node = document.querySelector(".cbi-section-node");
if (!node) return false;
let changed = false;
const all_node = node.querySelectorAll("[id]");
const reg1 = /^cbid\..*node\.main$/;
for (const el of all_node) {
if (!reg1.test(el.id)) continue;
if (el.querySelector(".node-actions")) continue;
const cbid = el.id.replace(/\.main$/, "");
const hidden_select = document.getElementById(cbid);
const node_select_value = hidden_select?.options[0]?.value;
if (!node_select_value) {
continue;
}
let html = '<a href="#" onclick="return to_edit_node(this);"><%:Edit%></a>';
const m = cbid.match(/\.node$/);
if (m) {
html += '<a href="#" onclick="window.open(\'' + '<%=api.url("get_redir_log") .. "?id=" .. sid%>' + '\', \'_blank\')"><%:Log%></a>';
}
html = '<div class="node-actions" style="display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; margin-left:4px;">' + html + '</div>'
el.insertAdjacentHTML("beforeend", html);
changed = true;
}
return changed;
}
const target = document.querySelector('form') || document.body;
const observer = new MutationObserver(() => go() ? observer.disconnect() : 0);
observer.observe(target, { childList: true, subtree: true });
const timer = setInterval(() => go() ? (clearInterval(timer), observer.disconnect()) : 0, 300);
setTimeout(() => { clearInterval(timer); observer.disconnect(); }, 5000);
});
(function () {
const startTime = Date.now();
const TIMEOUT = 3000;
const waitForDnsSelect = () => {
const dns_select = document.querySelector("select[id*='dns_shunt']");
if (dns_select) {
initDnsSelect(dns_select);
return;
}
if (Date.now() - startTime >= TIMEOUT) {
return;
}
requestAnimationFrame(waitForDnsSelect);
};
waitForDnsSelect();
const initDnsSelect = (dns_select) => {
if (dns_select.value === "chinadns-ng") {
addLogLink(dns_select);
}
if (dns_select._dnsLogBinded) return;
dns_select._dnsLogBinded = true;
dns_select.addEventListener("change", () => {
const existingLogLink = dns_select.parentElement.querySelector("a.dns-log-link");
if (existingLogLink) {
existingLogLink.remove();
}
if (dns_select.value === "chinadns-ng") {
addLogLink(dns_select);
}
});
};
const addLogLink = (select) => {
if (select.parentElement.querySelector("a.dns-log-link")) {
return;
}
const logLink = document.createElement("a");
logLink.innerHTML = "<%:Log%>";
logLink.href = "#";
logLink.className = "dns-log-link";
logLink.style.marginLeft = "10px";
logLink.setAttribute("onclick", "window.open('" + '<%=api.url("get_chinadns_log") .. "?flag=" .. sid%>' + "', '_blank')");
select.insertAdjacentElement("afterend", logLink);
};
})();
//]]>
</script>
@@ -1,59 +0,0 @@
<%
local map = self.map
local api = map.api
local appname = map.config
local section = self.section
-%>
<script type="text/javascript">
//<![CDATA[
const shunt_list = JSON.parse('<%=self.shunt_list%>');
document.addEventListener("DOMContentLoaded", () => {
const id = "cbid.<%=appname%>.<%=section%>.node";
let node = null;
const onChange = (e) => {
const new_val = e.target.value;
const new_hasItem = shunt_list.some(element => element.id == new_val);
if (new_hasItem) {
XHR.get('<%=api.url("update_config")%>', {
id: "<%=section%>",
data: JSON.stringify({
node: new_val,
mode: "1"
})
}, function(x, data) {
if (x && x.status == 200 && data.code == 1) {
window.location.reload();
} else {
alert("<%:Error%>");
}
});
} else {
document.getElementById("cbi-<%=appname%>-shunt_option_list")?.style.setProperty("display", "none");
}
};
const check = () => {
const el = document.getElementById(id);
if (el === node) return;
if (node && !el) {
document.getElementById("cbi-<%=appname%>-shunt_option_list")?.style.setProperty("display", "none");
} else {
let o_hasItem = shunt_list.some(element => element.id == el.value);
if (o_hasItem) {
document.getElementById("cbi-<%=appname%>-shunt_option_list")?.style.setProperty("display", "");
}
}
node = el;
if (node) {
node.addEventListener("change", onChange);
}
};
const observer = new MutationObserver(check);
observer.observe(document.body, {
childList: true,
subtree: true
});
check();
});
//]]>
</script>
@@ -65,13 +65,13 @@ window.dl_dropdown_data["<%=cbid%>"] = <%=json.stringify(dropdown_data)%>;
</script>
<div id="<%=cbid%>.main" class="dl-dropdown-container">
<select id="<%=cbid%>.ref" class="cbi-input-select" style="display:none !important;">
<option value>placeholder</option>
</select>
<div style="display:none !important;">
<select id="<%=cbid%>.ref" class="cbi-input-select" style="display:none !important;">
<option value>placeholder</option>
</select>
</div>
<!-- 已选节点显示区 -->
<div id="<%=cbid%>.selected" class="dl-selected-list"></div>
<!-- 模拟 listvalue 控件外观 -->
<div class="cbi-input-value cbi-input-select dl-dropdown-display" id="<%=cbid%>.display" tabindex="0">
<span id="<%=cbid%>.label" class="dl-dropdown-label">
@@ -90,9 +90,11 @@ window.lv_dropdown_data["<%=cbid%>"] = <%=json.stringify(dropdown_data)%>;
<div id="<%=cbid%>.main" class="lv-dropdown-container">
<!-- 隐藏 select(保存实际配置值) -->
<select id="<%=cbid%>" name="<%=cbid%>" class="cbi-input-select" data-update="change" style="display:none !important;">
<option value="<%=current_key%>" selected="selected">placeholder</option>
</select>
<div style="display:none !important;">
<select id="<%=cbid%>" name="<%=cbid%>" class="cbi-input-select" data-update="change" style="display:none !important;">
<option value="<%=current_key%>" selected="selected">placeholder</option>
</select>
</div>
<!-- 模拟 ListValue 控件外观 -->
<div class="cbi-input-value cbi-input-select lv-dropdown-display" id="<%=cbid%>.display" tabindex="0">
<span id="<%=cbid%>.label" class="lv-dropdown-label" title="<%=pcdata(current_label)%>">
@@ -64,9 +64,11 @@ end
%>
<div id="<%=cbid%>" class="cbi-input-select" style="display:inline-block;">
<select id="<%=cbid%>.ref" class="cbi-input-select" style="display:none !important;">
<option value>placeholder</option>
</select>
<div style="display:none !important;">
<select id="<%=cbid%>.ref" class="cbi-input-select" style="display:none !important;">
<option value>placeholder</option>
</select>
</div>
<!-- 搜索框 -->
<input type="text" id="<%=cbid%>.search"
class="mv_search_input cbi-input-text"
@@ -97,9 +97,11 @@ window.v_dropdown_data["<%=cbid%>"] = <%=json.stringify(dropdown_data)%>;
</script>
<div id="<%=cbid%>.v-main" class="v-dropdown-container">
<select id="<%=cbid%>.ref" class="cbi-input-select" style="display:none !important;">
<option value>placeholder</option>
</select>
<div style="display:none !important;">
<select id="<%=cbid%>.ref" class="cbi-input-select" style="display:none !important;">
<option value>placeholder</option>
</select>
</div>
<!-- 隐藏 input(保存实际配置值) -->
<input type="text" id="<%=cbid%>" name="<%=cbid%>" value="<%=current_key%>" data-update="change" style="display:none !important;" />
<!-- 模拟 ListValue 控件外观 -->
@@ -14,11 +14,6 @@ local sid = "@global[0]"
}
}
.shunt-tab-hidden {
display: none !important;
visibility: hidden !important;
}
@media screen and (max-width: 1152px) {
#cbi-<%=appname%>-socks .cbi-section-table-titles {
display: none !important;
@@ -28,7 +23,6 @@ local sid = "@global[0]"
<script type="text/javascript">
//<![CDATA[
let global_id = "<%=sid%>";
const shunt_list = JSON.parse('<%=self.shunt_list%>');
function to_edit_node(btn, stype) {
if (!btn) return;
@@ -235,47 +229,5 @@ local sid = "@global[0]"
select.insertAdjacentElement("afterend", logLink);
};
})();
document.addEventListener("DOMContentLoaded", () => setTimeout(() => {
const hiddenSelect = document.getElementById("cbid.<%=appname%>.<%=sid%>.node");
let o_val = hiddenSelect.value
const o_hasItem = shunt_list.find(element => element.id == o_val);
hiddenSelect.addEventListener("change", function(el){
let new_val = el.target.value
const new_hasItem = shunt_list.find(element => element.id == new_val);
if (new_hasItem) {
XHR.get('<%=api.url("update_config")%>', {
id: "<%=sid%>",
data: JSON.stringify({
node: new_val
})
},
function(x, data) {
if (x && x.status == 200 && data.code == 1) {
window.location.reload();
}
else {
alert("<%:Error%>");
}
});
} else {
try {
document.getElementById("tab.<%=appname%>.<%=sid%>.Shunt").classList.add('shunt-tab-hidden');
document.getElementById("tab.<%=appname%>.<%=sid%>.Shunt").style.display = "none";
} catch (error) {}
try {
document.querySelector('li[data-tab*="Shunt"]').classList.add('shunt-tab-hidden');
document.querySelector('li[data-tab*="Shunt"]').style.display = "none";
} catch (error) {}
}
});
let shunt_taboption = document.getElementById("container.<%=appname%>.<%=sid%>.Shunt");
if (shunt_taboption) {
let shunt_option_list = document.getElementById("cbi-<%=appname%>-shunt_option_list");
if (shunt_option_list) {
shunt_taboption.appendChild(shunt_option_list);
}
}
}, 100));
//]]>
</script>
@@ -1,14 +1,14 @@
<div class="cbi-value" id="cbi-<%=self.config.."-"..section.."-"..self.option%>" data-index="<%=self.index%>" data-depends="<%=pcdata(self:deplist2json(section))%>">
<label class="cbi-value-title">
<%:Switch Mode%>
</label>
<div class="cbi-value-field">
<div>
<input class="btn cbi-button cbi-button-apply" type="button" onclick="switch_gfw_mode()" value="<%:GFW List%>" />
<input class="btn cbi-button cbi-button-apply" type="button" onclick="switch_chnroute_mode()" value="<%:Not China List%>" />
<input class="btn cbi-button cbi-button-apply" type="button" onclick="switch_returnhome_mode()" value="<%:China List%>" />
<input class="btn cbi-button cbi-button-apply" type="button" onclick="switch_global_mode()" value="<%:Global Proxy%>" />
</div>
<%+cbi/valueheader%>
<label class="cbi-value-title">
<%:Switch Mode%>
</label>
<div class="cbi-value-field">
<div>
<input class="btn cbi-button cbi-button-apply" type="button" onclick="switch_gfw_mode()" value="<%:GFW List%>" />
<input class="btn cbi-button cbi-button-apply" type="button" onclick="switch_chnroute_mode()" value="<%:Not China List%>" />
<input class="btn cbi-button cbi-button-apply" type="button" onclick="switch_returnhome_mode()" value="<%:China List%>" />
<input class="btn cbi-button cbi-button-apply" type="button" onclick="switch_global_mode()" value="<%:Global Proxy%>" />
</div>
</div>
@@ -108,3 +108,4 @@
opt.set("udp_proxy_mode", "proxy");
}
</script>
<%+cbi/valuefooter%>
@@ -0,0 +1,73 @@
<%
local map = self.map
local api = map.api
local config = map.config
local verify_option = self.verify_option
local section_id = verify_option.section.section
-%>
<% if verify_option then -%>
<script type="text/javascript">
//<![CDATA[
document.addEventListener("DOMContentLoaded", () => {
const shunt_list = JSON.parse('<%=self.shunt_list%>');
const dom_node_id = "<%=verify_option:cbid(section_id)%>";
let uci_node_val = "";
const onChange = (e) => {
const new_val = e.target.value;
const to_shunt = shunt_list.some(element => element.id == new_val);
if (to_shunt) {
const map = document.getElementById("cbi-<%=config%>");
const hiddenInput = document.createElement('input');
hiddenInput.type = 'hidden';
hiddenInput.name = 'node_save_before';
hiddenInput.value = uci_node_val;
map.appendChild(hiddenInput);
const saveBtn = document.getElementsByClassName('cbi-button-save');
if (saveBtn && saveBtn.length > 0) {
saveBtn[saveBtn.length - 1].click();
}
/*
XHR.get('<%=api.url("update_config")%>', {
id: "<%=section_id%>",
data: JSON.stringify({
node: new_val,
mode: "1"
})
}, function(x, data) {
if (x && x.status == 200 && data.code == 1) {
window.location.reload();
} else {
alert("<%:Error%>");
}
});
*/
} else {
try { shunt.hide(); } catch (error) {}
}
};
const check = () => {
const el = document.getElementById(dom_node_id);
if (!el) {
try { shunt.hide(); } catch (error) {}
return;
}
let is_shunt = shunt_list.some(element => element.id == el.value);
if (is_shunt) {
try { shunt.show(); } catch (error) {}
}
if (!el.hasEventListener) {
uci_node_val = el.value;
el.addEventListener("change", onChange);
el.hasEventListener = true;
}
};
const observer = new MutationObserver(check);
observer.observe(document.body, {
childList: true,
subtree: true
});
check();
});
//]]>
</script>
<%- end %>
@@ -2,18 +2,22 @@
local map = self.map
local api = map.api
local config = map.config
local id = self.id
local s_cfgid = self.s_cfgid
local node_id = self.node_id
local verify_option = self.verify_option
local section_id = self.section_id
local section_type = self.section_type
local tab_name = self.tab_name
-%>
<style>
.hidden {
.shunt_hidden {
display: none !important;
visibility: hidden !important;
}
</style>
<script type="text/javascript">
//<![CDATA[
const normal_list = JSON.parse('<%=self.normal_list%>');
var normal_list = JSON.parse('<%=self.normal_list%>');
function check_fakedns(id, val) {
const dom_id = "cbid.table." + id + ".";
const fakedns_dom = document.getElementById(dom_id + "_fakedns");
@@ -21,12 +25,14 @@ local s_cfgid = self.s_cfgid
const selectNode = document.getElementById(dom_id + "_node");
val = selectNode.value;
}
if (val == "" || val == "_direct") {
fakedns_dom.value = 0;
fakedns_dom.checked = false;
fakedns_dom.classList.add('hidden');
} else {
fakedns_dom.classList.remove('hidden');
if (fakedns_dom) {
if (val == "" || val == "_direct") {
fakedns_dom.value = 0;
fakedns_dom.checked = false;
fakedns_dom.classList.add('shunt_hidden');
} else {
fakedns_dom.classList.remove('shunt_hidden');
}
}
}
function check_proxy_tag(id, val) {
@@ -45,13 +51,13 @@ local s_cfgid = self.s_cfgid
const searchInput = document.getElementById(cbid + ".search");
const listContainer = document.getElementById(cbid + ".list");
lv_change(cbid, listContainer, hiddenSelect, labelSpan, searchInput, "", "<%:Close (Not use)%>");
proxy_tag_dom.classList.add('hidden');
proxy_tag_dom?.classList.add('shunt_hidden');
} else {
proxy_tag_dom.classList.remove('hidden');
proxy_tag_dom?.classList.remove('shunt_hidden');
}
}
function refresh_depends() {
const table_dom = document.getElementById("cbi-passwall-shunt_option_list");
const table_dom = document.getElementById("cbi-<%=config%>-<%=section_type%>");
if (table_dom) {
const trs = table_dom.getElementsByTagName("tr");
for (let i = 1; i < trs.length;i++) {
@@ -60,40 +66,82 @@ local s_cfgid = self.s_cfgid
}
}
}
const shunt = {
tab() {
// Move the shunt options to Tab
<% if tab_name then -%>
const shunt_taboption = document.getElementById("container.<%=config%>.<%=section_id%>.<%=tab_name%>");
if (shunt_taboption) {
const shunt_option_list = document.getElementById("cbi-<%=config%>-<%=section_type%>");
if (shunt_option_list) {
shunt_taboption.appendChild(shunt_option_list);
}
}
<%- end %>
},
hide() {
document.getElementById("cbi-<%=config%>-<%=section_type%>")?.classList.add('shunt_hidden');
<% if tab_name then -%>
try { document.getElementById("tab.<%=config%>.<%=section_id%>.<%=tab_name%>")?.classList.add('shunt_hidden'); } catch (error) {}
try { document.querySelector('li[data-tab*="<%=tab_name%>"]')?.classList.add('shunt_hidden'); } catch (error) {}
const shunt_taboption = document.getElementById("container.<%=config%>.<%=section_id%>.<%=tab_name%>");
if (shunt_taboption) {
[...shunt_taboption.querySelectorAll('[name]')].forEach(el => {
const nameValue = el.getAttribute('name');
el.setAttribute('o-name', nameValue);
el.removeAttribute('name');
});
}
<%- end %>
},
show() {
document.getElementById("cbi-<%=config%>-<%=section_type%>")?.classList.remove('shunt_hidden');
<% if tab_name then -%>
try { document.getElementById("tab.<%=config%>.<%=section_id%>.<%=tab_name%>")?.classList.remove('shunt_hidden'); } catch (error) {}
try { document.querySelector('li[data-tab*="<%=tab_name%>"]')?.classList.remove('shunt_hidden'); } catch (error) {}
const shunt_taboption = document.getElementById("container.<%=config%>.<%=section_id%>.<%=tab_name%>");
if (shunt_taboption) {
[...shunt_taboption.querySelectorAll('[o-name]')].forEach(el => {
const nameValue = el.getAttribute('o-name');
el.setAttribute('name', nameValue);
el.removeAttribute('o-name');
});
}
<%- end %>
}
}
document.addEventListener("DOMContentLoaded", function () {
shunt.tab();
waitForElement('select[name*="<%=config%>"][name*="shunt_group"]', function(el) {
let o_val = el.value;
el.addEventListener("change", () => {
el.blur();
if (o_val != el.value) {
let save = true;
if (save) {
update_config("<%=id%>", {
shunt_group: getOption("<%=config%>", "<%=s_cfgid%>", "shunt_group").value,
}, get_current_url());
}
update_config("<%=node_id%>", {
shunt_group: getOption("<%=config%>", "<%=section_id%>", "shunt_group").value,
}, get_current_url());
}
});
});
});
document.addEventListener("DOMContentLoaded", () => setTimeout(() => {
refresh_depends();
const table_dom = document.getElementById("cbi-passwall-shunt_option_list");
if (table_dom) {
const trs = table_dom.getElementsByTagName("tr");
for (let i = 1; i < trs.length;i++) {
const selectNode = document.getElementById("cbid.table." + i + "._node.main");
selectNode.addEventListener("change", function(el){
let new_val = el.target.value
check_fakedns(i, new_val);
check_proxy_tag(i, new_val);
});
if (i == trs.length - 1) {
const newArray = window.lv_dropdown_data["cbid.table." + i + "._node"].ungrouped.filter(item => item.key !== "_default");
window.lv_dropdown_data["cbid.table." + i + "._node"].ungrouped = newArray
setTimeout(() => {
refresh_depends();
const table_dom = document.getElementById("cbi-<%=config%>-<%=section_type%>");
if (table_dom) {
const trs = table_dom.getElementsByTagName("tr");
for (let i = 1; i < trs.length;i++) {
const selectNode = document.getElementById("cbid.table." + i + "._node.main");
selectNode.addEventListener("change", function(el){
let new_val = el.target.value
check_fakedns(i, new_val);
check_proxy_tag(i, new_val);
});
if (i == trs.length - 1) {
const newArray = window.lv_dropdown_data["cbid.table." + i + "._node"].ungrouped.filter(item => item.key !== "_default");
window.lv_dropdown_data["cbid.table." + i + "._node"].ungrouped = newArray
}
}
}
}
}, 100));
}, 200)
});
//]]>
</script>
@@ -26,36 +26,45 @@ local config = map.config
update_config(node_id, {
remarks: getOption("<%=config%>", "<%=sid%>", "remarks").value,
group: getOption("<%=config%>", "<%=sid%>", "group").value,
type: getOption("<%=config%>", "<%=sid%>", "type").value,
type: el.value,
//protocol: ''
}, node_config_url);
}
}
});
const fromUrlCache = JSON.parse(sessionStorage.getItem("fromUrl"));
if (fromUrlCache && fromUrlCache.savetime && (Date.now() - fromUrlCache.timestamp) < fromUrlCache.savetime) {
fromUrl(fromUrlCache.link, true)
} else {
sessionStorage.removeItem("fromUrl");
}
});
waitForElement('select[name*="<%=config%>"][name*="protocol"]', function(el) {
let o_val = el.value;
const name_split = el.name.split(".");
const name = name_split[name_split.length - 1];
el.addEventListener("change", () => {
el.blur();
if (el.value.startsWith("_") || (o_val.startsWith("_") && !el.value.startsWith("_"))) {
let name_split = el.name.split(".");
let name = name_split[name_split.length - 1];
let save = true;
if (save) {
/*
const saveBtn = document.getElementsByClassName('cbi-button-save');
if (saveBtn && saveBtn.length > 0) {
saveBtn[saveBtn.length - 1].click();
}
*/
update_config(node_id, {
remarks: getOption("<%=config%>", "<%=sid%>", "remarks").value,
group: getOption("<%=config%>", "<%=sid%>", "group").value,
protocol: getOption("<%=config%>", "<%=sid%>", name).value,
}, node_config_url);
} else {
window.location.href = node_config_url + "?select_proto=" + encodeURIComponent(el.value);
if (el.value.startsWith("_")) {
const map = document.getElementById("cbi-<%=config%>");
const hiddenInput = document.createElement('input');
hiddenInput.type = 'hidden';
hiddenInput.name = 'load' + el.value;
hiddenInput.value = '1';
map.appendChild(hiddenInput);
}
const saveBtn = document.getElementsByClassName('cbi-button-save');
if (saveBtn && saveBtn.length > 0) {
saveBtn[saveBtn.length - 1].click();
}
/*
update_config(node_id, {
remarks: getOption("<%=config%>", "<%=sid%>", "remarks").value,
group: getOption("<%=config%>", "<%=sid%>", "group").value,
protocol: el.value,
}, node_config_url);
*/
}
});
});
@@ -1,8 +1,8 @@
<%+cbi/valueheader%>
<%
local api = require "luci.passwall.api"
local appname = api.appname
local map = self.map
local api = map.api
local config = map.config
local has_ss_rust = api.is_finded("sslocal")
local has_singbox = api.finded_com("sing-box")
@@ -17,7 +17,7 @@ local hysteria2_type = api.get_core("hysteria2_type", {{has_hysteria2,"hysteria2
local current_node = map:get(section)
-%>
<script src="<%=resource%>/view/<%=appname%>/qrcode.min.js"></script>
<script src="<%=resource%>/view/<%=api.appname%>/qrcode.min.js"></script>
<script type="text/javascript">//<![CDATA[
let has_singbox = "<%=has_singbox%>"
let has_xray = "<%=has_xray%>"
@@ -112,8 +112,7 @@ local current_node = map:get(section)
function buildUrl(btn, urlname, sid) {
var opt = {
base: "cbid.<%=appname%>",
client: true,
base: "cbid.<%=config%>",
get: function (opt) {
var id = this.base + "." + opt;
var obj = document.getElementsByName(id)[0] || document.getElementsByClassName(id)[0] || document.getElementById(id)
@@ -158,7 +157,6 @@ local current_node = map:get(section)
}
}
opt.base = "cbid." + urlname + "." + sid;
opt.client = urlname.indexOf("server") === -1;
var v_type = opt.get("type").value;
var v_alias = opt.get("remarks");
@@ -753,34 +751,41 @@ local current_node = map:get(section)
return server.replace(/^\[/, '').replace(/\]$/, '');
}
function fromUrl(btn, urlname, sid, cacheData) {
var opt = {
base: 'cbid.<%=appname%>',
client: true,
function tofromUrl(btn) {
let link = prompt('<%:Paste Share URL Here%>', '');
if (link === null || link === "") {
return false;
}
return fromUrl(link);
}
function fromUrl(link, from_cache) {
if (!link) {
return false;
}
const s = document.getElementById("<%=self.option%>-status");
if (!s) {
alert("Never");
return false;
}
s.innerHTML = "";
const current_type = '<%=current_node.type or ""%>';
let switch_type = null;
const opt = {
base: "cbid.<%=self.option%>.<%=self.value%>",
get: function (opt) {
var obj;
var id = this.base + '.' + opt;
obj = document.getElementsByName(id)[0] || document.getElementById(id);
if (obj) {
var combobox = document.getElementById('cbi.combobox.' + id);
if (combobox) {
obj.combobox = combobox;
}
var div = document.getElementById(id);
if (div && div.getElementsByTagName("li").length > 0) {
obj = div;
}
return obj;
} else {
return null;
}
return getOption("<%=config%>", "<%=self.value%>", opt);
},
set: function (opt, val) {
if (!switch_type && opt == "type" && current_type != val) {
switch_type = val;
return;
}
var obj;
obj = this.get(opt);
if (obj) {
var event = document.createEvent("HTMLEvents");
event.initEvent("change", true, true);
if (obj.type === 'checkbox') {
obj.checked = val;
} else {
@@ -814,17 +819,17 @@ local current_node = map:get(section)
}
}
try {
obj.dispatchEvent(event);
obj.dispatchEvent(new Event("change", { bubbles: true, cancelable: true }));
} catch (err) {}
} else {
//alert('<%:Faltal on set option, please help in debug: %>' + opt + ' = ' + val);
// 处理 DynamicList
// Processing DynamicList
var fullName = this.base + '.' + opt;
var lists = document.querySelectorAll('.cbi-dynlist');
for (var i = 0; i < lists.length; i++) {
var parent = lists[i].closest('.cbi-value');
if (!parent) continue;
// 尝试从 label for 属性中提取 fullName
// Try to extract fullName from the label's for attribute.
var label = parent.querySelector('label.cbi-value-title');
var labelFor = label?.getAttribute('for');
if (labelFor === fullName) {
@@ -838,57 +843,20 @@ local current_node = map:get(section)
}
}
}
},
setlist: function (opt, vlist) {
var id = this.base + "." + opt;
var objs = document.getElementsByName(id) || document.getElementsByClassName(id);
if (objs) {
var values = "";
for (var i = 0; i < vlist.length; i++) {
values += vlist[i] + ", ";
}
alert("Manually input the option:\n" + opt + "s:\n[" + values + "]");
} else {
//alert("<%:Faltal on set option, please help in debug: %>" + opt);
}
}
}
var s = document.getElementById(urlname + '-status');
if (!s) {
alert("Never");
return false;
}
opt.base = "cbid." + urlname + "." + sid;
opt.client = urlname.indexOf("server") === -1;
var ssrurl = null;
if (cacheData) {
ssrurl = cacheData.ssrurl
} else {
ssrurl = prompt('<%:Paste Share URL Here%>', '');
if (ssrurl === null || ssrurl === "") {
return false;
}
sessionStorage.setItem("fromUrl", JSON.stringify({
timestamp: Date.now(),
savetime: 60 * 1000,
urlname: urlname,
sid: sid,
ssrurl: ssrurl
}));
}
ssrurl = ssrurl.replace(/&amp;/gi, '&').replace(/\s*#\s*/, '#').trim(); //一些奇葩的链接用"&amp;"当做"&""#"前后带空格
s.innerHTML = "";
var ssu = ssrurl.split('://');
var event = document.createEvent("HTMLEvents");
event.initEvent("change", true, true);
link = link.split(/\r\n|\r|\n/, 1)[0];
link = link.replace(/&amp;/gi, '&').replace(/\s*#\s*/, '#').trim(); //一些奇葩的链接用"&amp;"当做"&""#"前后带空格
const link_split = link.split('://');
const link_type = link_split[0];
var dom_prefix = null
let dom_prefix = null;
if (ssu[0] === "ssr") {
if (link_type === "ssr") {
dom_prefix = "ssr_"
//var b64c = ssu[1].match(/([A-Za-z0-9_-]+)/);
var sstr = b64decsafe((ssu[1] || "").replace(/#.*/, "").trim());
//var b64c = link_split[1].match(/([A-Za-z0-9_-]+)/);
var sstr = b64decsafe((link_split[1] || "").replace(/#.*/, "").trim());
var ploc = sstr.indexOf("/?");
var url0 = "", param = "";
if (ploc > 0) {
@@ -923,8 +891,8 @@ local current_node = map:get(section)
if (typeof (rem) !== 'undefined' && rem !== '' && rem.length > 0)
opt.set('remarks', b64decutf8safe(rem));
}
if (ssu[0] === "ss") {
var url0 = ssu[1] || "";
if (link_type === "ss") {
var url0 = link_split[1] || "";
param = "";
var ploc = url0.indexOf("#");
@@ -999,15 +967,15 @@ local current_node = map:get(section)
queryParam.type && queryParam.type !== "tcp" &&
queryParam.headerType && queryParam.headerType !== "none";
if (has_xray && (ss_type == "xray" || needUpgrade || queryParam.type === "xhttp")) {
dom_prefix = "xray_"
dom_prefix = "xray_";
opt.set('type', "Xray");
opt.set(dom_prefix + 'protocol', "shadowsocks");
} else if (has_singbox && (ss_type == "sing-box" || needUpgrade)) {
dom_prefix = "singbox_"
dom_prefix = "singbox_";
opt.set('type', "sing-box");
opt.set(dom_prefix + 'protocol', "shadowsocks");
} else {
dom_prefix = "ssrust_"
dom_prefix = "ssrust_";
opt.set('type', "SS-Rust");
}
@@ -1207,17 +1175,17 @@ local current_node = map:get(section)
}
}
}
if (ssu[0] === "trojan") {
if (link_type === "trojan") {
if (trojan_type == "sing-box" && has_singbox) {
dom_prefix = "singbox_"
dom_prefix = "singbox_";
opt.set('type', "sing-box");
opt.set(dom_prefix + 'protocol', "trojan");
} else if (trojan_type == "xray" && has_xray) {
dom_prefix = "xray_"
dom_prefix = "xray_";
opt.set('type', "Xray");
opt.set(dom_prefix + 'protocol', "trojan");
}
var m = parseNodeUrl(ssrurl);
var m = parseNodeUrl(link);
var password = m.passwd;
if (password === "") {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
@@ -1345,14 +1313,14 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
}
}
if (ssu[0] === "vmess") {
var sstr = b64DecodeUnicode((ssu[1] || "").replace(/#.*/, "").trim());
if (link_type === "vmess") {
var sstr = b64DecodeUnicode((link_split[1] || "").replace(/#.*/, "").trim());
var ploc = sstr.indexOf("/?");
if (vmess_type == "sing-box" && has_singbox) {
dom_prefix = "singbox_"
dom_prefix = "singbox_";
opt.set('type', "sing-box");
} else if (has_xray) {
dom_prefix = "xray_"
dom_prefix = "xray_";
opt.set('type', "Xray");
}
opt.set(dom_prefix + 'protocol', "vmess");
@@ -1446,14 +1414,14 @@ local current_node = map:get(section)
opt.set(dom_prefix + 'use_finalmask', !!ssm.fm);
opt.set(dom_prefix + 'finalmask', ssm.fm || "");
}
if (ssu[0] === "vless") {
if (link_type === "vless") {
if (vless_type == "sing-box" && has_singbox) {
dom_prefix = "singbox_";
} else if (has_xray) {
dom_prefix = "xray_";
}
var m = parseNodeUrl(ssrurl);
var m = parseNodeUrl(link);
var password = m.passwd;
if (password === "") {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
@@ -1475,7 +1443,7 @@ local current_node = map:get(section)
if (["xhttp", "kcp", "mkcp"].includes(queryParam.type) && vless_type !== "xray" && has_xray) {
dom_prefix = "xray_";
}
opt.set("type", dom_prefix === "singbox_" ? "sing-box" : "Xray");
opt.set('type', dom_prefix == "singbox_" ? "sing-box" : "Xray");
opt.set(dom_prefix + 'protocol', "vless");
opt.set(dom_prefix + 'uuid', password);
@@ -1605,8 +1573,8 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
}
}
if (ssu[0] === "hysteria2" || ssu[0] === "hy2") {
var m = parseNodeUrl(ssrurl);
if (link_type === "hysteria2" || link_type === "hy2") {
var m = parseNodeUrl(link);
var password = m.passwd;
if (password === "") {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
@@ -1671,13 +1639,13 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
}
}
if (ssu[0] === "tuic") {
if (link_type === "tuic") {
if (has_singbox) {
dom_prefix = "singbox_"
dom_prefix = "singbox_";
opt.set('type', "sing-box");
}
opt.set(dom_prefix + 'protocol', "tuic");
var _parsedUrl = new URL("http://" + ssu[1]);
var _parsedUrl = new URL("http://" + link_split[1]);
var username = _parsedUrl.username;
var password = _parsedUrl.password;
var hostname = _parsedUrl.hostname;
@@ -1720,13 +1688,13 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(hash.substr(1)));
}
}
if (ssu[0] === "anytls") {
if (link_type === "anytls") {
if (has_singbox) {
dom_prefix = "singbox_"
dom_prefix = "singbox_";
opt.set('type', "sing-box");
}
opt.set(dom_prefix + 'protocol', "anytls");
var m = parseNodeUrl(ssrurl);
var m = parseNodeUrl(link);
var password = m.passwd;
if (password === "") {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
@@ -1776,13 +1744,13 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
}
}
if (ssu[0] === "naive+https" || ssu[0] === "naive+quic") {
if (link_type === "naive+https" || link_type === "naive+quic") {
if (has_singbox) {
dom_prefix = "singbox_"
dom_prefix = "singbox_";
opt.set('type', "sing-box");
}
opt.set(dom_prefix + 'protocol', "naive");
var _parsedUrl = new URL("http://" + ssu[1]);
var _parsedUrl = new URL("http://" + link_split[1]);
var username = _parsedUrl.username;
var password = _parsedUrl.password;
var hostname = _parsedUrl.hostname;
@@ -1817,7 +1785,7 @@ local current_node = map:get(section)
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || '');
opt.set(dom_prefix + 'ech', !!queryParam.ech);
opt.set(dom_prefix + 'ech_config', queryParam.ech || '');
if (ssu[0] === "naive+quic") {
if (link_type === "naive+quic") {
opt.set(dom_prefix + 'naive_quic', true);
opt.set(dom_prefix + 'naive_congestion_control', queryParam.congestion_control || 'bbr');
}
@@ -1825,16 +1793,25 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(hash.substr(1)));
}
}
if (dom_prefix && dom_prefix != null) {
if (opt.get(dom_prefix + 'port').value) {
opt.get(dom_prefix + 'port').focus();
opt.get(dom_prefix + 'port').blur();
}
} else {
if (switch_type) {
sessionStorage.setItem("fromUrl", JSON.stringify({
timestamp: Date.now(),
savetime: 60 * 1000,
link: link
}));
opt.set("type", switch_type);
return
}
if (!dom_prefix) {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
sessionStorage.removeItem("fromUrl");
return false;
}
if (opt.get(dom_prefix + 'port')) {
opt.get(dom_prefix + 'port').focus();
opt.get(dom_prefix + 'port').blur();
}
s.innerHTML = "<font color='green'><%:Import Finished %></font>";
sessionStorage.removeItem("fromUrl");
return true;
@@ -1844,17 +1821,6 @@ local current_node = map:get(section)
window.open('<%=api.url("gen_client_config")%>?id=' + sid, "_blank")
}
document.addEventListener("DOMContentLoaded", function () {
setTimeout(function () {
const fromUrlCache = JSON.parse(sessionStorage.getItem("fromUrl"));
if (fromUrlCache && fromUrlCache.savetime && (Date.now() - fromUrlCache.timestamp) < fromUrlCache.savetime) {
fromUrl(null, fromUrlCache.urlname, fromUrlCache.sid, fromUrlCache)
} else {
sessionStorage.removeItem("fromUrl");
}
}, 500);
})
//]]></script>
<style>
.share-toolbar {
@@ -1887,7 +1853,7 @@ local current_node = map:get(section)
<div class="share-toolbar">
<div class="toolbar-buttons">
<% if not current_node.protocol or not current_node.protocol:find("^_") then -%>
<input id="fromUrlBtn" type="button" class="btn cbi-button cbi-button-apply" value='<%:From Share URL%>' onclick="return fromUrl(this, '<%=self.option%>', '<%=self.value%>')" />
<input id="fromUrlBtn" type="button" class="btn cbi-button cbi-button-apply" value='<%:From Share URL%>' onclick="return tofromUrl()" />
<input id="exportUrlBtn" type="button" class="btn cbi-button cbi-button-apply" value='<%:Build Share URL%>' onclick="return exportUrl(this, '<%=self.option%>', '<%=self.value%>')" />
<input id="genQrcodeBtn" type="button" class="btn cbi-button cbi-button-apply" value='<%:Generate QRCode%>' onclick="return genQrcode(this, '<%=self.option%>', '<%=self.value%>')" />
<% end -%>
@@ -5,20 +5,21 @@ local appname = map.config
local is_js_luci = api.is_js_luci()
local section_tag = is_js_luci and "div" or "fieldset"
local default_node_type = ""
local shunt_rule_list = {}
local node = map:get("@global[0]", "node")
if node then
local node_type = map:get(node, "type")
local node_protocol = map:get(node, "protocol")
if (node_type == "Xray" or node_type == "sing-box") and node_protocol == "_shunt" then
default_node_type = node_protocol
if node_protocol == "_shunt" then
local node_shunt_group = map:get(node, "shunt_group")
map:foreach("shunt_rules", function(e)
if e[".name"] and e.remarks and e.group == node_shunt_group then
shunt_rule_list[#shunt_rule_list + 1] = e
end
end)
shunt_rule_list[#shunt_rule_list + 1] = {
[".name"] = "default_node",
remarks = translate("Default")
}
end
end
-%>
@@ -1401,6 +1402,7 @@ table td, .table .td {
var node_tr_html = "";
for (var i = 0; i < group_nodes[group].length; i++) {
let o = group_nodes[group][i]
let _port = o["port"] || o["hysteria_hop"] || o["hysteria2_hop"];
var newDom = node_template.cloneNode(true);
newDom.classList.add("cbi-rowstyle-" + (i % 2 + 1));
var innerHTML = newDom.innerHTML;
@@ -1414,17 +1416,15 @@ table td, .table .td {
} else {
innerHTML = innerHTML.split("{{tcping}}").join('<span class="tcping_value" cbiid="{{id}}"><a href="javascript:void(0)" style="color:inherit">---</a></span>');
}
let is_realm = (o.type === "Hysteria2" || o.protocol === 'hysteria2') && o.hysteria2_realms || false;
if (o["protocol"] === '_shunt' || is_realm) {
innerHTML = innerHTML.split("{{url_test}}").join('<span class="ping" cbiid="{{id}}">---</span>');
} else {
if (o["address"] && _port) {
innerHTML = innerHTML.split("{{url_test}}").join('<span class="ping"><a href="javascript:void(0)" onclick="javascript:urltest_node(\'{{id}}\', this)" title="<%:TLS handshake test, latency for reference only%>"><%:Test%></a></span>');
} else {
innerHTML = innerHTML.split("{{url_test}}").join('<span class="ping" cbiid="{{id}}">---</span>');
}
innerHTML = innerHTML.split("{{id}}").join(o[".name"]);
innerHTML = innerHTML.split("{{group}}").join(o["group"] || "");
let node_remarks = get_remarks_name(o);
if (show_node_info == "1") {
let _port = o["port"] || o["hysteria_hop"] || o["hysteria2_hop"];
if (_port) _port = _port.replace(/:/g, '-');
if (o["address"] && _port) {
let _address = o["address"]
@@ -1671,14 +1671,13 @@ table td, .table .td {
<input class="btn cbi-button" type="button" onclick="set_node_div.set('@global[0]', 'node')" value="<%:OK%>" />
</div>
</div>
<% if default_node_type == "_shunt" then %>
<% if #shunt_rule_list > 0 then %>
<div id="set_node_shunt_div" style="margin: 0.5em 0; padding: 0.5em; background: #f7f7f7; border-radius: 4px;">
<div class="cbi-section-descr" style="text-align: left;"><a style="color:#FF8C00"><%:Set as the routing rule node:%></a></div>
<div>
<% for i, v in ipairs(shunt_rule_list) do %>
<input class="btn cbi-button" type="button" onclick="set_node_div.set('<%=node%>', '<%=v[".name"]%>')" value="<%=v.remarks%>" />
<% end %>
<input class="btn cbi-button" type="button" onclick="set_node_div.set('<%=node%>', 'default_node')" value="<%:Default%>" />
</div>
</div>
<% end %>
+3 -30
View File
@@ -1253,6 +1253,9 @@ msgstr "访问控制"
msgid "ACLs is a tools which used to designate specific IP proxy mode."
msgstr "访问控制列表是用于指定特殊 IP 代理模式的工具。"
msgid "Use global configuration when using the global node."
msgstr "与全局节点相同时,将使用全局配置。"
msgid "Example:"
msgstr "例:"
@@ -1640,36 +1643,6 @@ msgstr "启用节点日志"
msgid "Log Level"
msgstr "日志等级"
msgid "Advanced log feature"
msgstr "高级日志功能"
msgid "For professionals only."
msgstr "仅限专业人士使用。"
msgid "Persist log file directory"
msgstr "持久性日志文件目录"
msgid "The path to the directory used to store persist log files, the \"/\" at the end can be omitted. Leave it blank to disable this feature."
msgstr "用来存储持久性日志文件的目录路径,末尾的 “/” 可以省略。留空以禁用此功能。"
msgid "Logging to system log"
msgstr "记录到系统日志"
msgid "Logging to the system log for more advanced functions. For example, send logs to a dedicated log server."
msgstr "将日志记录到系统日志,以实现更加高级的功能。例如,把日志发送到专门的日志服务器。"
msgid "Log Event Filter"
msgstr "日志事件过滤器"
msgid "Support regular expression."
msgstr "支持正则表达式。"
msgid "Shell Command"
msgstr "Shell 命令"
msgid "Shell command to execute, replace log content with %s."
msgstr "要执行的 Shell 命令,用 %s 代替日志内容。"
msgid "Not enabled log"
msgstr "未启用日志"
@@ -1,17 +1,12 @@
#!/bin/sh
# Devices without a hardware RTC boot with a wrong system clock: sysfixtime can
# only restore the mtime of the newest file under /etc, so the clock is usually
# hours behind after a cold boot. passwall is then started by
# /etc/hotplug.d/iface/98-passwall on ifup, which typically happens before NTP
# has corrected the time, and time-sensitive handshakes (VMess AEAD, TLS) fail.
#
# Nothing restarts passwall once the clock is corrected, so it stays broken
# until the user restarts it manually. Restart once when ntpd reports that the
# time is valid -- the same approach dnsmasq uses for DNSSEC in
# /etc/hotplug.d/ntp/25-dnsmasqsec.
[ "$ACTION" = "step" ] || exit 0
[ "$ACTION" = "stratum" ] || exit 0
offset=${offset#-}
offset=${offset%.*}
# Skip service restart if the time adjustment is less than 120 seconds.
[ "$offset" -lt 120 ] && exit 0
. /usr/share/passwall/utils.sh
@@ -23,5 +18,5 @@ NTP_LOCK_FILE="${LOCK_PATH}/${CONFIG}_ntp.lock"
echo $$ > ${NTP_LOCK_FILE}
/etc/init.d/${CONFIG} restart >/dev/null 2>&1 &
logger -p notice -t network -s "${CONFIG}: restart after NTP time became valid"
logger -p notice -t network -s "${CONFIG}: restart after NTP time step (${offset}s)"
}
+191 -184
View File
@@ -87,7 +87,7 @@ run_ipt2socks() {
run_singbox() {
local flag type node redir_port tcp_proxy_way socks_address socks_port socks_username socks_password http_address http_port http_username http_password
local dns_listen_port direct_dns_query_strategy direct_dns_port direct_dns_udp_server direct_dns_tcp_server remote_dns_protocol remote_dns_udp_server remote_dns_tcp_server remote_dns_doh remote_dns_client_ip remote_fakedns remote_dns_query_strategy remote_rewrite_ttl dns_cache dns_socks_address dns_socks_port
local loglevel log_file config_file server_host server_port no_run use_proxy_list use_gfw_list chn_list
local loglevel log_file config_file server_host server_port no_run use_proxy_list use_gfw_list chn_list run_in_global
eval_set_val "$@"
[ -z "$type" ] && {
type=$(echo $(config_n_get $node type) | tr 'A-Z' 'a-z')
@@ -182,13 +182,17 @@ run_singbox() {
[ "$remote_fakedns" = "1" ] && json_add_string "remote_dns_fake" "1"
[ -n "$remote_rewrite_ttl" ] && json_add_string "remote_rewrite_ttl" "${remote_rewrite_ttl}"
[ -n "$no_run" ] && json_add_string "no_run" "1"
[ -n "$run_in_global" ] && json_add_string "run_in_global" "1"
local _json_arg="$(json_dump)"
lua $UTIL_SINGBOX gen_config "${_json_arg}" > $config_file
[ -n "$no_run" ] && return
local test_log_file=$log_file
local status=0
[ "$test_log_file" = "/dev/null" ] && test_log_file="${TMP_PATH}/${config_file##*/}_test.log"
$SINGBOX_BIN check -c "$config_file" > $test_log_file 2>&1; local status=$?
[ -n "$run_in_global" ] && {
$SINGBOX_BIN check -c "$config_file" > $test_log_file 2>&1; status=$?
}
if [ "${status}" = 0 ]; then
ln_run "$SINGBOX_BIN" "sing-box" "${log_file}" run -c "$config_file"
else
@@ -201,7 +205,7 @@ run_singbox() {
run_xray() {
local flag type node redir_port tcp_proxy_way socks_address socks_port socks_username socks_password http_address http_port http_username http_password
local dns_listen_port direct_dns_query_strategy direct_dns_port direct_dns_udp_server direct_dns_tcp_server remote_dns_protocol remote_dns_udp_server remote_dns_tcp_server remote_dns_doh remote_dns_client_ip remote_fakedns remote_dns_query_strategy dns_cache dns_socks_address dns_socks_port
local loglevel log_file config_file server_host server_port no_run use_proxy_list use_gfw_list chn_list
local loglevel log_file config_file server_host server_port no_run use_proxy_list use_gfw_list chn_list run_in_global
eval_set_val "$@"
[ -z "$type" ] && {
type=$(echo $(config_n_get $node type) | tr 'A-Z' 'a-z')
@@ -278,13 +282,17 @@ run_xray() {
json_add_string "loglevel" "$loglevel"
[ -n "$no_run" ] && json_add_string "no_run" "1"
[ -n "$run_in_global" ] && json_add_string "run_in_global" "1"
local _json_arg="$(json_dump)"
lua $UTIL_XRAY gen_config "${_json_arg}" > $config_file
[ -n "$no_run" ] && return
local test_log_file=$log_file
local status=0
[ "$test_log_file" = "/dev/null" ] && test_log_file="${TMP_PATH}/${config_file##*/}_test.log"
$XRAY_BIN run -test -c "$config_file" > $test_log_file; local status=$?
[ -n "$run_in_global" ] && {
$XRAY_BIN run -test -c "$config_file" > $test_log_file; status=$?
}
if [ "${status}" = 0 ]; then
ln_run "$XRAY_BIN" "xray" "${log_file}" run -c "$config_file"
else
@@ -337,10 +345,10 @@ run_socks() {
[ -n "$config_file" ] && [ -z "$(echo ${config_file} | grep $TMP_PATH)" ] && config_file=$TMP_PATH/$config_file
[ -n "$http_port" ] || http_port=0
[ -n "$http_config_file" ] && [ -z "$(echo ${http_config_file} | grep $TMP_PATH)" ] && http_config_file=$TMP_PATH/$http_config_file
if [ -n "$log_file" ] && [ "$log_file" != "/dev/null" ] && [ -z "$(echo ${log_file} | grep $TMP_PATH)" ]; then
log_file=$TMP_PATH/$log_file
else
if [ -z "$log_file" ] || [ "$log_file" = "/dev/null" ]; then
log_file="/dev/null"
elif [ "${log_file#"$TMP_PATH/"}" = "$log_file" ]; then
log_file=$TMP_PATH/$log_file
fi
local type=$(echo $(config_n_get $node type) | tr 'A-Z' 'a-z')
@@ -489,8 +497,6 @@ run_socks() {
;;
esac
set_cache_var "node_${node}_socks_port" "${socks_port}"
# http to socks
[ -z "$http_flag" ] && [ "$http_port" != "0" ] && [ -n "$http_config_file" ] && [ "$type" != "sing-box" ] && [ "$type" != "xray" ] && [ "$type" != "socks" ] && {
json_init
@@ -566,11 +572,7 @@ start_global() {
local node_socks_bind_local=$(config_n_get @global[0] node_socks_bind_local 1)
local node_socks_bind="127.0.0.1"
[ "${node_socks_bind_local}" != "1" ] && node_socks_bind="0.0.0.0"
local global_socks_port=$(config_n_get @global[0] node_socks_port 1070)
GLOBAL_SOCKS_port=$(get_new_port $global_socks_port)
if [ "$GLOBAL_SOCKS_port" != "$global_socks_port" ]; then
echolog "注意:全局节点 Socks 端口冲突,原端口 ${global_socks_port} 已自动更改为 ${GLOBAL_SOCKS_port}"
fi
GLOBAL_SOCKS_port=$(config_n_get @global[0] node_socks_port 1070)
GLOBAL_HTTP_port=$(config_n_get @global[0] node_http_port 0)
[ "$GLOBAL_HTTP_port" != "0" ] && local on_node_http=1
if [ $PROXY_IPV6 = "1" ]; then
@@ -605,7 +607,7 @@ start_global() {
;;
sing-box)
local _flag="global"
local _args=""
local _args="run_in_global=1"
[ "$on_node_socks" = "1" ] && {
node_socks_flag=1
_args="${_args} socks_address=${node_socks_bind} socks_port=${GLOBAL_SOCKS_port}"
@@ -683,7 +685,7 @@ start_global() {
;;
xray)
local _flag="global"
local _args=""
local _args="run_in_global=1"
[ "$on_node_socks" = "1" ] && {
node_socks_flag=1
_args="${_args} socks_address=${node_socks_bind} socks_port=${GLOBAL_SOCKS_port}"
@@ -833,7 +835,6 @@ start_global() {
set_cache_var "GLOBAL_SOCKS_server" "${GLOBAL_SOCKS_server}"
}
[ "$type" != "sing-box" ] && [ "$type" != "xray" ] && echo "${NODE}" >> $TMP_PATH/direct_node_list
set_cache_var "node_${NODE}_redir_port" "$REDIR_PORT"
set_cache_var "ACL_GLOBAL_node" "$NODE"
set_cache_var "ACL_GLOBAL_redir_port" "$REDIR_PORT"
}
@@ -1382,6 +1383,7 @@ acl_app() {
log=${log:-0}
loglevel=${loglevel:-warn}
log_chinadns_ng=${log_chinadns_ng:-0}
if [ -n "${sources}" ]; then
for s in $sources; do
@@ -1437,7 +1439,10 @@ acl_app() {
[ -n "$node" ] && {
local GLOBAL_NODE=$(get_cache_var "ACL_GLOBAL_node")
[ -n "${GLOBAL_NODE}" ] && GLOBAL_redir_port=$(get_cache_var "ACL_GLOBAL_redir_port")
[ -n "${GLOBAL_NODE}" ] && {
local GLOBAL_redir_port=$(get_cache_var "ACL_GLOBAL_redir_port")
[ "$node" = "${GLOBAL_NODE}" ] && node="default"
}
if [ "$node" = "default" ]; then
if [ -n "${GLOBAL_NODE}" ]; then
set_cache_var "ACL_${sid}_node" "${GLOBAL_NODE}"
@@ -1449,187 +1454,189 @@ acl_app() {
echolog " - 全局节点未启用,跳过【${remarks}"
fi
else
[ "$(config_get_type $node)" = "nodes" ] || [ "$(config_get_type $node)" = "socks" ] && {
if [ -n "${GLOBAL_NODE}" ] && [ "$node" = "${GLOBAL_NODE}" ]; then
set_cache_var "ACL_${sid}_node" "${GLOBAL_NODE}"
set_cache_var "ACL_${sid}_redir_port" "${GLOBAL_redir_port}"
set_cache_var "ACL_${sid}_dns_port" "${GLOBAL_DNSMASQ_PORT}"
set_cache_var "ACL_${sid}_default" "1"
[ "$GLOBAL_SHUNT_NODE_FAKEDNS" = "1" ] && use_fakedns=1
([ "$(config_get_type $node)" = "nodes" ] || [ "$(config_get_type $node)" = "socks" ]) && {
local type protocol
if [ "$(config_get_type $node)" = "socks" ]; then
if [ "${dns_mode}" = "xray" ]; then
type="xray"
elif [ "${dns_mode}" = "sing-box" ]; then
type="sing-box"
elif [ -n "${SINGBOX_BIN}" ]; then
type="sing-box"
elif [ -n "${XRAY_BIN}" ]; then
type="xray"
fi
else
local type protocol
if [ "$(config_get_type $node)" = "socks" ]; then
if [ "${dns_mode}" = "xray" ]; then
type="xray"
elif [ "${dns_mode}" = "sing-box" ]; then
type="sing-box"
elif [ -n "${SINGBOX_BIN}" ]; then
type="sing-box"
elif [ -n "${XRAY_BIN}" ]; then
type="xray"
type=$(echo $(config_n_get $node type) | tr 'A-Z' 'a-z')
protocol=$(config_n_get $node protocol)
fi
([ "$type" = "sing-box" ] || [ "$type" = "xray" ]) && [ "$protocol" = "_shunt" ] && [ "$type" != "$dns_mode" ] && {
dns_mode=$type
[ "$type" = "xray" ] && {
case "$v2ray_dns_mode" in http3|tls|quic)
v2ray_dns_mode="tcp"
remote_dns="1.1.1.1#53"
;;
esac
}
}
local dns_cache_str="${dns_mode}_${v2ray_dns_mode}_${remote_dns}_${remote_dns_doh}_${remote_dns_client_ip}_${remote_fakedns}_${remote_rewrite_ttl}"
dns_cache_key="$(echo -n "${dns_cache_str}" | md5sum | cut -d " " -f1)"
if [ "$remote_fakedns" = "1" ] || ([ "$protocol" = "_shunt" ] && [ "$(config_n_get $node fakedns)" = "1" ]); then
use_fakedns=1
fi
run_dns() {
local dns_fwd_port=$1
[ -z "$dns_fwd_port" ] && {
dns_port=$(get_new_port $(expr $dns_port + 1))
dns_fwd_port=$dns_port
if [ "$dns_mode" = "dns2socks" ]; then
run_dns2socks flag=acl_${sid} socks_address=127.0.0.1 socks_port=$socks_port listen_address=0.0.0.0 listen_port=$dns_fwd_port dns=$remote_dns cache=1
elif [ "$dns_mode" = "sing-box" ] || [ "$dns_mode" = "xray" ]; then
config_file=$TMP_ACL_PATH/${node}_SOCKS_${socks_port}_DNS.json
remote_dns_doh=${remote_dns_doh:-https://1.1.1.1/dns-query}
local type=${dns_mode}
[ "${dns_mode}" = "sing-box" ] && type="singbox"
dnsmasq_filter_proxy_ipv6=0
remote_dns_query_strategy="UseIP"
[ "$filter_proxy_ipv6" = "1" ] && {
remote_dns_query_strategy="UseIPv4"
set_cache_var "node_${node}_${dns_cache_key}_ipv6" "0"
}
run_${type} flag=acl_${sid} type=$dns_mode dns_socks_address=127.0.0.1 dns_socks_port=$socks_port dns_listen_port=$dns_fwd_port \
remote_dns_protocol=${v2ray_dns_mode} remote_dns_udp_server=${remote_dns} remote_dns_tcp_server=${remote_dns} remote_dns_doh="${remote_dns_doh}" \
remote_dns_query_strategy=${remote_dns_query_strategy} remote_dns_client_ip=${remote_dns_client_ip} config_file=$config_file
fi
else
type=$(echo $(config_n_get $node type) | tr 'A-Z' 'a-z')
protocol=$(config_n_get $node protocol)
fi
([ "$type" = "sing-box" ] || [ "$type" = "xray" ]) && [ "$protocol" = "_shunt" ] && [ "$type" != "$dns_mode" ] && {
dns_mode=$type
[ "$type" = "xray" ] && {
case "$v2ray_dns_mode" in http3|tls|quic)
v2ray_dns_mode="tcp"
remote_dns="1.1.1.1#53"
;;
esac
}
}
dns_cache_key="${dns_mode}_${remote_dns}_${v2ray_dns_mode:-none}_${remote_dns_client_ip:-0}_${remote_fakedns:-0}_${remote_rewrite_ttl:-30}"
([ "$v2ray_dns_mode" = "doh" ] || [ "$v2ray_dns_mode" = "http3" ]) && {
dns_cache_key="${dns_mode}_${remote_dns_doh:-https://1.1.1.1/dns-query}_${v2ray_dns_mode:-doh}_${remote_dns_client_ip:-0}_${remote_fakedns:-0}_${remote_rewrite_ttl:-30}"
set_cache_var "node_${node}_${dns_cache_key}" "$dns_fwd_port"
}
if [ "$remote_fakedns" = "1" ] || ([ "$protocol" = "_shunt" ] && [ "$(config_n_get $node fakedns)" = "1" ]); then
use_fakedns=1
fi
[ "$dns_shunt" = "chinadns-ng" ] && [ -n "$(first_type chinadns-ng)" ] && {
chinadns_ng_min=2024.04.13
chinadns_ng_now=$($(first_type chinadns-ng) -V | grep -i "ChinaDNS-NG " | awk '{print $2}')
if [ $(check_ver "$chinadns_ng_now" "$chinadns_ng_min") = 1 ]; then
echolog " * 注意:当前 ChinaDNS-NG 版本为[ $chinadns_ng_now ],请更新到[ $chinadns_ng_min ]或以上版本,否则 DNS 有可能无法正常工作!"
fi
run_dns() {
local _dns_port
[ -n $1 ] && _dns_port=$1
[ -z ${_dns_port} ] && {
[ "$filter_proxy_ipv6" = "1" ] && dnsmasq_filter_proxy_ipv6=0
chinadns_port=$(expr $chinadns_port + 1)
_china_ng_listen="127.0.0.1#${chinadns_port},::1#${chinadns_port}"
_chinadns_local_dns=$(IFS=','; set -- $LOCAL_DNS; [ "${1%%[#:]*}" = "127.0.0.1" ] && echo "$1" || ([ -n "$2" ] && echo "$1,$2" || echo "$1"))
_direct_dns_mode=$(config_n_get @global[0] direct_dns_mode "auto")
case "${_direct_dns_mode}" in
udp)
_chinadns_local_dns=$(normalize_dns "$(config_n_get @global[0] direct_dns 223.5.5.5:53)")
;;
tcp)
_chinadns_local_dns="tcp://$(normalize_dns "$(config_n_get @global[0] direct_dns 223.5.5.5:53)")"
;;
esac
run_chinadns_ng \
_flag="$sid" \
_listen_port=${chinadns_port} \
_dns_local=${_chinadns_local_dns} \
_dns_trust=127.0.0.1#${dns_fwd_port} \
_no_ipv6_trust=${filter_proxy_ipv6} \
_use_direct_list=${use_direct_list} \
_use_proxy_list=${use_proxy_list} \
_use_block_list=${use_block_list} \
_gfwlist=${use_gfw_list} \
_chnlist=${chn_list} \
_default_mode=${tcp_proxy_mode} \
_default_tag=${chinadns_ng_default_tag:-smart} \
_no_logic_log=1 \
_node=${node} \
_filter_https=${force_https_soa:-0} \
_log=${log_chinadns_ng}
use_default_dns="chinadns_ng"
}
([ "$filter_proxy_ipv6" = "1" ] && [ "$dnsmasq_filter_proxy_ipv6" = "1" ]) && {
dnsmasq_filter_proxy_ipv6=$(get_cache_var "node_${node}_${dns_cache_key}_ipv6")
dnsmasq_filter_proxy_ipv6=${dnsmasq_filter_proxy_ipv6:-1}
}
dnsmasq_port=$(get_new_port $(expr $dnsmasq_port + 1))
local dnsmasq_conf=${acl_path}/dnsmasq.conf
local dnsmasq_conf_path=${acl_path}/dnsmasq.d
lua $APP_PATH/helper_dnsmasq.lua add_rule -FLAG ${sid} -TMP_DNSMASQ_PATH ${dnsmasq_conf_path} -DNSMASQ_CONF_FILE ${dnsmasq_conf} \
-LISTEN_PORT ${dnsmasq_port} -DEFAULT_DNS ${DEFAULT_DNS} -LOCAL_DNS $LOCAL_DNS \
-USE_DIRECT_LIST "${use_direct_list}" -USE_PROXY_LIST "${use_proxy_list}" -USE_BLOCK_LIST "${use_block_list}" -USE_GFW_LIST "${use_gfw_list}" -CHN_LIST "${chn_list}" \
-TUN_DNS "127.0.0.1#${dns_fwd_port}" -USE_DEFAULT_DNS "${use_default_dns:-direct}" -CHINADNS_DNS ${_china_ng_listen:-0} \
-NODE $node -DEFAULT_PROXY_MODE ${tcp_proxy_mode} -NO_PROXY_IPV6 ${dnsmasq_filter_proxy_ipv6:-0} -NFTFLAG ${nftflag:-0} \
-NO_LOGIC_LOG 1
ln_run "$(first_type dnsmasq)" "dnsmasq_${sid}" "/dev/null" -C ${dnsmasq_conf} -x ${acl_path}/dnsmasq.pid
set_cache_var "ACL_${sid}_dns_port" "${dnsmasq_port}"
#dhcp.leases to hosts
$APP_PATH/lease2hosts.sh > /dev/null 2>&1 &
}
local _redir_port=$(get_cache_var "acl_node_${node}_redir_port")
local _socks_port=$(get_cache_var "acl_node_${node}_socks_port")
local _enable_log=$(get_cache_var "acl_node_${node}_enable_log")
local _dns_port
if [ -n "${_socks_port}" ] && [ -n "${_redir_port}" ] && [ "${_enable_log}" != "1" ] && [ "${log}" != "1" ]; then
socks_port=${_socks_port}
node_port=${_redir_port}
_dns_port=$(get_cache_var "node_${node}_${dns_cache_key}")
run_dns ${_dns_port}
else
socks_port=$(get_new_port $(expr $socks_port + 1))
set_cache_var "acl_node_${node}_socks_port" "${socks_port}"
redir_port=$(get_new_port $(expr $redir_port + 1))
set_cache_var "acl_node_${node}_redir_port" "${redir_port}"
node_port=$redir_port
local log_file="/dev/null"
[ "${log}" = "1" ] && {
log_file="${TMP_ACL_PATH}/${sid}/node.log"
set_cache_var "acl_node_${node}_enable_log" "1"
}
if [ "${type}" = "sing-box" ] || [ "${type}" = "xray" ]; then
config_file="acl/${node}_${redir_port}.json"
_extra_param="socks_address=127.0.0.1 socks_port=$socks_port"
[ "${type}" = "${dns_mode}" ] && {
dns_port=$(get_new_port $(expr $dns_port + 1))
_dns_port=$dns_port
if [ "$dns_mode" = "dns2socks" ]; then
run_dns2socks flag=acl_${sid} socks_address=127.0.0.1 socks_port=$socks_port listen_address=0.0.0.0 listen_port=${_dns_port} dns=$remote_dns cache=1
elif [ "$dns_mode" = "sing-box" ] || [ "$dns_mode" = "xray" ]; then
config_file=$TMP_ACL_PATH/${node}_SOCKS_${socks_port}_DNS.json
remote_dns_doh=${remote_dns_doh:-https://1.1.1.1/dns-query}
local type=${dns_mode}
[ "${dns_mode}" = "sing-box" ] && type="singbox"
dnsmasq_filter_proxy_ipv6=0
remote_dns_query_strategy="UseIP"
[ "$filter_proxy_ipv6" = "1" ] && remote_dns_query_strategy="UseIPv4"
run_${type} flag=acl_${sid} type=$dns_mode dns_socks_address=127.0.0.1 dns_socks_port=$socks_port dns_listen_port=${_dns_port} remote_dns_protocol=${v2ray_dns_mode} remote_dns_udp_server=${remote_dns} remote_dns_tcp_server=${remote_dns} remote_dns_doh="${remote_dns_doh}" remote_dns_query_strategy=${remote_dns_query_strategy} remote_dns_client_ip=${remote_dns_client_ip} config_file=$config_file
fi
set_cache_var "node_${node}_$(echo -n "${dns_cache_key}" | md5sum | cut -d " " -f1)" "${_dns_port}"
dnsmasq_filter_proxy_ipv6=0
remote_dns_query_strategy="UseIP"
[ "$filter_proxy_ipv6" = "1" ] && {
remote_dns_query_strategy="UseIPv4"
set_cache_var "node_${node}_${dns_cache_key}_ipv6" "0"
}
remote_dns_doh=${remote_dns_doh:-https://1.1.1.1/dns-query}
_extra_param="${_extra_param} dns_listen_port=${_dns_port} remote_dns_protocol=${v2ray_dns_mode} remote_dns_udp_server=${remote_dns} remote_dns_tcp_server=${remote_dns}"
_extra_param="${_extra_param} remote_dns_doh=${remote_dns_doh} remote_dns_query_strategy=${remote_dns_query_strategy} remote_fakedns=${remote_fakedns:-0} remote_dns_client_ip=${remote_dns_client_ip}"
}
[ "$dns_shunt" = "chinadns-ng" ] && [ -n "$(first_type chinadns-ng)" ] && {
chinadns_ng_min=2024.04.13
chinadns_ng_now=$($(first_type chinadns-ng) -V | grep -i "ChinaDNS-NG " | awk '{print $2}')
if [ $(check_ver "$chinadns_ng_now" "$chinadns_ng_min") = 1 ]; then
echolog " * 注意:当前 ChinaDNS-NG 版本为[ $chinadns_ng_now ],请更新到[ $chinadns_ng_min ]或以上版本,否则 DNS 有可能无法正常工作!"
fi
[ "$filter_proxy_ipv6" = "1" ] && dnsmasq_filter_proxy_ipv6=0
chinadns_port=$(expr $chinadns_port + 1)
_china_ng_listen="127.0.0.1#${chinadns_port},::1#${chinadns_port}"
_chinadns_local_dns=$(IFS=','; set -- $LOCAL_DNS; [ "${1%%[#:]*}" = "127.0.0.1" ] && echo "$1" || ([ -n "$2" ] && echo "$1,$2" || echo "$1"))
_direct_dns_mode=$(config_n_get @global[0] direct_dns_mode "auto")
case "${_direct_dns_mode}" in
udp)
_chinadns_local_dns=$(normalize_dns "$(config_n_get @global[0] direct_dns 223.5.5.5:53)")
;;
tcp)
_chinadns_local_dns="tcp://$(normalize_dns "$(config_n_get @global[0] direct_dns 223.5.5.5:53)")"
;;
esac
run_chinadns_ng \
_flag="$sid" \
_listen_port=${chinadns_port} \
_dns_local=${_chinadns_local_dns} \
_dns_trust=127.0.0.1#${_dns_port} \
_no_ipv6_trust=${filter_proxy_ipv6} \
_use_direct_list=${use_direct_list} \
_use_proxy_list=${use_proxy_list} \
_use_block_list=${use_block_list} \
_gfwlist=${use_gfw_list} \
_chnlist=${chn_list} \
_default_mode=${tcp_proxy_mode} \
_default_tag=${chinadns_ng_default_tag:-smart} \
_no_logic_log=1 \
_node=${node} \
_filter_https=${force_https_soa:-0} \
_log=${log}
use_default_dns="chinadns_ng"
_extra_param="${_extra_param} tcp_proxy_way=$TCP_PROXY_WAY"
config_file="$TMP_PATH/$config_file"
[ "${type}" = "sing-box" ] && {
type="singbox"
_extra_param="${_extra_param} remote_rewrite_ttl=${remote_rewrite_ttl:-30}"
}
dnsmasq_port=$(get_new_port $(expr $dnsmasq_port + 1))
local dnsmasq_conf=${acl_path}/dnsmasq.conf
local dnsmasq_conf_path=${acl_path}/dnsmasq.d
lua $APP_PATH/helper_dnsmasq.lua add_rule -FLAG ${sid} -TMP_DNSMASQ_PATH ${dnsmasq_conf_path} -DNSMASQ_CONF_FILE ${dnsmasq_conf} \
-LISTEN_PORT ${dnsmasq_port} -DEFAULT_DNS ${DEFAULT_DNS} -LOCAL_DNS $LOCAL_DNS \
-USE_DIRECT_LIST "${use_direct_list}" -USE_PROXY_LIST "${use_proxy_list}" -USE_BLOCK_LIST "${use_block_list}" -USE_GFW_LIST "${use_gfw_list}" -CHN_LIST "${chn_list}" \
-TUN_DNS "127.0.0.1#${_dns_port}" -USE_DEFAULT_DNS "${use_default_dns:-direct}" -CHINADNS_DNS ${_china_ng_listen:-0} \
-NODE $node -DEFAULT_PROXY_MODE ${tcp_proxy_mode} -NO_PROXY_IPV6 ${dnsmasq_filter_proxy_ipv6:-0} -NFTFLAG ${nftflag:-0} \
-NO_LOGIC_LOG 1
ln_run "$(first_type dnsmasq)" "dnsmasq_${sid}" "/dev/null" -C ${dnsmasq_conf} -x ${acl_path}/dnsmasq.pid
set_cache_var "ACL_${sid}_dns_port" "${dnsmasq_port}"
set_cache_var "node_${node}_$(echo -n "${tcp_proxy_mode}_${dns_cache_key}" | md5sum | cut -d " " -f1)" "${dnsmasq_port}"
#dhcp.leases to hosts
$APP_PATH/lease2hosts.sh > /dev/null 2>&1 &
}
_redir_port=$(get_cache_var "node_${node}_redir_port")
_socks_port=$(get_cache_var "node_${node}_socks_port")
if [ -n "${_socks_port}" ] && [ -n "${_redir_port}" ]; then
socks_port=${_socks_port}
node_port=${_redir_port}
_dnsmasq_port=$(get_cache_var "node_${node}_$(echo -n "${tcp_proxy_mode}_${dns_cache_key}" | md5sum | cut -d " " -f1)")
if [ -z "${_dnsmasq_port}" ]; then
_dns_port=$(get_cache_var "node_${node}_$(echo -n "${dns_cache_key}" | md5sum | cut -d " " -f1)")
run_dns ${_dns_port}
else
[ -n "${_dnsmasq_port}" ] && set_cache_var "ACL_${sid}_dns_port" "${_dnsmasq_port}"
fi
_extra_param="${_extra_param} use_proxy_list=$use_proxy_list use_gfw_list=$use_gfw_list chn_list=$chn_list"
run_${type} flag=$node node=$node redir_port=$redir_port ${_extra_param} config_file=$config_file log_file=$log_file loglevel=$loglevel
else
socks_port=$(get_new_port $(expr $socks_port + 1))
set_cache_var "node_${node}_socks_port" "${socks_port}"
redir_port=$(get_new_port $(expr $redir_port + 1))
set_cache_var "node_${node}_redir_port" "${redir_port}"
node_port=$redir_port
local log_file="/dev/null"
[ "${log}" = "1" ] && log_file="${TMP_ACL_PATH}/${sid}/node.log"
if [ "${type}" = "sing-box" ] || [ "${type}" = "xray" ]; then
config_file="acl/${node}_${redir_port}.json"
_extra_param="socks_address=127.0.0.1 socks_port=$socks_port"
[ "${type}" = "${dns_mode}" ] && {
dns_port=$(get_new_port $(expr $dns_port + 1))
_dns_port=$dns_port
dnsmasq_filter_proxy_ipv6=0
remote_dns_query_strategy="UseIP"
[ "$filter_proxy_ipv6" = "1" ] && remote_dns_query_strategy="UseIPv4"
remote_dns_doh=${remote_dns_doh:-https://1.1.1.1/dns-query}
_extra_param="${_extra_param} dns_listen_port=${_dns_port} remote_dns_protocol=${v2ray_dns_mode} remote_dns_udp_server=${remote_dns} remote_dns_tcp_server=${remote_dns}"
_extra_param="${_extra_param} remote_dns_doh=${remote_dns_doh} remote_dns_query_strategy=${remote_dns_query_strategy} remote_fakedns=${remote_fakedns:-0} remote_dns_client_ip=${remote_dns_client_ip}"
}
_extra_param="${_extra_param} tcp_proxy_way=$TCP_PROXY_WAY"
config_file="$TMP_PATH/$config_file"
[ "${type}" = "sing-box" ] && {
type="singbox"
_extra_param="${_extra_param} remote_rewrite_ttl=${remote_rewrite_ttl:-30}"
}
_extra_param="${_extra_param} use_proxy_list=$use_proxy_list use_gfw_list=$use_gfw_list chn_list=$chn_list"
run_${type} flag=$node node=$node redir_port=$redir_port ${_extra_param} config_file=$config_file log_file=$log_file loglevel=$loglevel
else
config_file="acl/${node}_SOCKS_${socks_port}.json"
run_socks flag=$node node=$node bind=127.0.0.1 socks_port=$socks_port config_file=$config_file log_file=$log_file
# log_file=$TMP_ACL_PATH/ipt2socks_${node}_${redir_port}.log
log_file="/dev/null"
run_ipt2socks flag=acl_${node} tcp_tproxy=${is_tproxy} local_port=$redir_port socks_address=127.0.0.1 socks_port=$socks_port log_file=$log_file
fi
run_dns ${_dns_port}
config_file="acl/${node}_SOCKS_${socks_port}.json"
run_socks flag=$node node=$node bind=127.0.0.1 socks_port=$socks_port config_file=$config_file log_file=$log_file
# log_file=$TMP_ACL_PATH/ipt2socks_${node}_${redir_port}.log
log_file="/dev/null"
run_ipt2socks flag=acl_${node} tcp_tproxy=${is_tproxy} local_port=$redir_port socks_address=127.0.0.1 socks_port=$socks_port log_file=$log_file
fi
set_cache_var "ACL_${sid}_node" "${node}"
set_cache_var "ACL_${sid}_redir_port" "${node_port}"
run_dns ${_dns_port}
fi
set_cache_var "ACL_${sid}_node" "${node}"
set_cache_var "ACL_${sid}_redir_port" "${node_port}"
}
fi
[ "${use_fakedns}" = "1" ] && set_cache_var "ACL_${sid}_fakedns" "1"
}
unset enabled sid remarks sources interface tcp_no_redir_ports udp_no_redir_ports use_global_config node use_direct_list use_proxy_list use_block_list use_gfw_list chn_list tcp_proxy_mode udp_proxy_mode filter_proxy_ipv6 dns_mode remote_dns v2ray_dns_mode remote_dns_doh remote_dns_client_ip
unset _ip _mac _iprange _ipset _ip_or_mac source_list node_port config_file _extra_param dns_cache_key log loglevel
unset _ip _mac _iprange _ipset _ip_or_mac source_list node_port config_file _extra_param dns_cache_key log loglevel log_chinadns_ng
unset _china_ng_listen _chinadns_local_dns _direct_dns_mode chinadns_ng_default_tag dnsmasq_filter_proxy_ipv6 remote_fakedns force_https_soa use_fakedns remote_rewrite_ttl
done
unset socks_port redir_port dns_port dnsmasq_port chinadns_port
@@ -1844,8 +1851,8 @@ get_local_dns() {
RESOLVFILE=/tmp/resolv.conf.d/resolv.conf.auto
[ -f "${RESOLVFILE}" ] && [ -s "${RESOLVFILE}" ] || RESOLVFILE=/tmp/resolv.conf.auto
ISP_DNS=$(cat $RESOLVFILE 2>/dev/null | grep -E -o "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" | grep -v -E '^(0\.0\.0\.0|127\.0\.0\.1)$' | awk '!seen[$0]++')
ISP_DNS6=$(cat $RESOLVFILE 2>/dev/null | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | awk -F % '{print $1}' | awk -F " " '{print $2}' | grep -v -Fx ::1 | grep -v -Fx :: | awk '!seen[$0]++')
ISP_DNS=$(grep -Eo "$IPv4_REGEX" "$RESOLVFILE" 2>/dev/null | grep -Ev '^(0\.0\.0\.0|127\.0\.0\.1)$' | awk '!seen[$0]++')
ISP_DNS6=$(grep -Eo "$IPv6_REGEX" "$RESOLVFILE" 2>/dev/null | awk -F% '{print $1}' | grep -Ev '^::1$|^::$' | awk '!seen[$0]++')
DEFAULT_DNS=$(uci show dhcp.@dnsmasq[0] | grep "\.server=" | awk -F '=' '{print $2}' | sed "s/'//g" | tr ' ' '\n' | grep -v "\/" | sed ':label;N;s/\n/,/;b label')
[ -z "${DEFAULT_DNS}" ] && [ "$(echo $ISP_DNS | tr ' ' '\n' | wc -l)" -ge 1 ] && DEFAULT_DNS=$(echo -n $ISP_DNS | tr ' ' '\n' | tr '\n' ',' | sed 's/,$//')
@@ -109,20 +109,26 @@ api.uci_foreach_c("haproxy_config", function(t)
t.origin_port = server_port
if health_check_type == "script_logic" then
if server_node.type ~= "Socks" then
local relay_port = server_node.port
local new_port = api.get_new_port()
local config_file = string.format("%s_%s.json", t[".name"], new_port)
sys.call(string.format('/usr/share/%s/app.sh run_socks "%s"> /dev/null',
appname,
string.format("flag=%s node=%s bind=%s socks_port=%s config_file=%s",
new_port, --flag
server_node[".name"], --node
"127.0.0.1", --bind
new_port, --socks port
config_file --config file
local new_port
local cache = api.get_socks_port_by_cache(server_node[".name"])
if cache then
new_port = cache
else
new_port = api.get_new_port()
local config_file = string.format("%s_%s.json", t[".name"], new_port)
sys.call(string.format('/usr/share/%s/app.sh run_socks "%s"> /dev/null',
appname,
string.format("flag=%s node=%s bind=%s socks_port=%s config_file=%s",
new_port, --flag
server_node[".name"], --node
"127.0.0.1", --bind
new_port, --socks port
config_file --config file
)
)
)
)
api.set_socks_port_to_cache(server_node[".name"], new_port)
end
server_address = "127.0.0.1"
server_port = new_port
end
@@ -236,6 +236,12 @@ load_acl() {
}
[ -n "$(get_cache_var "ACL_${sid}_dns_port")" ] && dns_redirect_port=$(get_cache_var "ACL_${sid}_dns_port")
[ -n "$(get_cache_var "ACL_${sid}_fakedns")" ] && use_fakedns=$(get_cache_var "ACL_${sid}_fakedns")
([ -n "$node" ] && ([ "$node" = "default" ] || [ "$node" = "$NODE" ])) && {
use_global_config=1
unset node
}
[ -n "$node" ] && {
if [ "$(config_get_type $node)" = "socks" ]; then
node_remark="Socks 配置($(config_n_get $node port) 端口)"
@@ -385,16 +391,16 @@ load_acl() {
local shunt_id
for shunt_id in $shunt_ids; do
[ "${shunt_group}" != "$(config_n_get ${shunt_id} group)" ] && continue
config_n_get $shunt_id ip_list | tr -s "\r\n" "\n" | grep -v "^#" | sed -e "/^$/d" | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | sed -e "s/^/add $shunt_set_name &/g" -e "s/$/ timeout 0/g" | ipset -! -R
config_n_get $shunt_id ip_list | tr -s "\r\n" "\n" | grep -v "^#" | sed -e "/^$/d" | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "s/^/add $shunt6_set_name &/g" -e "s/$/ timeout 0/g" | ipset -! -R
config_n_get $shunt_id ip_list | tr -s "\r\n" "\n" | sed 's/#.*//' | grep -E "$IPv4_REGEX" | sed -e "s/^/add $shunt_set_name &/g" -e "s/$/ timeout 0/g" | ipset -! -R
config_n_get $shunt_id ip_list | tr -s "\r\n" "\n" | sed 's/#.*//' | grep -E "$IPv6_REGEX" | sed -e "s/^/add $shunt6_set_name &/g" -e "s/$/ timeout 0/g" | ipset -! -R
[ "$USE_GEOVIEW" = "1" ] && {
local geoip_code=$(config_n_get $shunt_id ip_list | tr -s "\r\n" "\n" | sed -e "/^$/d" | grep -E "^geoip:" | grep -v "^geoip:private" | sed -E 's/^geoip:(.*)/\1/' | sed ':a;N;$!ba;s/\n/,/g')
[ -n "$geoip_code" ] && GEOIP_CODE="${GEOIP_CODE:+$GEOIP_CODE,}$geoip_code"
}
done
if [ -n "$GEOIP_CODE" ]; then
get_geoip $GEOIP_CODE ipv4 | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | sed -e "s/^/add $shunt_set_name &/g" -e "s/$/ timeout 0/g" | ipset -! -R
get_geoip $GEOIP_CODE ipv6 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "s/^/add $shunt6_set_name &/g" -e "s/$/ timeout 0/g" | ipset -! -R
get_geoip $GEOIP_CODE ipv4 | sed -e "s/^/add $shunt_set_name &/g" -e "s/$/ timeout 0/g" | ipset -! -R
get_geoip $GEOIP_CODE ipv6 | sed -e "s/^/add $shunt6_set_name &/g" -e "s/$/ timeout 0/g" | ipset -! -R
fi
}
}
@@ -411,7 +417,7 @@ load_acl() {
[ "$_ipv4" != "1" ] && $ip6t_n -A PSW_DNS $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j REDIRECT --to-ports ${dns_redirect} 2>/dev/null
$ipt_n -A PSW_DNS $(comment "$remarks") -p tcp ${_ipt_source} --dport 53 -j REDIRECT --to-ports ${dns_redirect}
[ "$_ipv4" != "1" ] && $ip6t_n -A PSW_DNS $(comment "$remarks") -p tcp ${_ipt_source} --dport 53 -j REDIRECT --to-ports ${dns_redirect} 2>/dev/null
[ -z "$(get_cache_var "ACL_${sid}_default")" ] && echolog " - ${msg}使用与全局配置不相同节点,已将DNS强制重定向到专用 DNS 服务器。"
[ -z "$(get_cache_var "ACL_${sid}_default")" ] && echolog " - ${msg}节点不同于全局配置,DNS 重定向到专用服务器[${dns_redirect}]"
fi
[ -n "$tcp_port" ] || [ -n "$udp_port" ] && {
@@ -757,12 +763,12 @@ filter_haproxy() {
filter_vpsip() {
local vps_addrs=$(uci show $CONFIG | grep -E "(\.address=|\.download_address=|\.domain_resolver_dns=|\.domain_resolver_dns_https=)" | cut -d "'" -f 2 | grep -Ev "$EXCLUDE_VPSIP")
local ipv4_addrs=$(echo "$vps_addrs" | grep -Eo "([0-9]{1,3}\.){3}[0-9]{1,3}")
local ipv4_addrs=$(echo "$vps_addrs" | grep -Eo "$IPv4_REGEX")
[ -n "$ipv4_addrs" ] && {
echo "$ipv4_addrs" | sed "s/^/add $IPSET_VPS /" | awk '1; END{print "COMMIT"}' | ipset -! -R
echolog " - [$?]加入所有IPv4节点服务器IP到ipset[$IPSET_VPS]直连完成"
}
local ipv6_addrs=$(echo "$vps_addrs" | grep -Eo "\[?[A-Fa-f0-9:]*:[A-Fa-f0-9:]+\]?")
local ipv6_addrs=$(echo "$vps_addrs" | grep -Eo "$IPv6_REGEX")
[ -n "$ipv6_addrs" ] && {
echo "$ipv6_addrs" | sed "s/^/add $IPSET_VPS6 /" | awk '1; END{print "COMMIT"}' | ipset -! -R
echolog " - [$?]加入所有IPv6节点服务器IP到ipset[$IPSET_VPS6]直连完成"
@@ -770,8 +776,8 @@ filter_vpsip() {
#订阅方式为直连时
local subscribe_host=$(get_subscribe_host | grep -Ev "$EXCLUDE_VPSIP")
[ -n "$subscribe_host" ] && {
echo "$subscribe_host" | grep -Eo "([0-9]{1,3}\.){3}[0-9]{1,3}" | sed "s/^/add $IPSET_VPS /" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
echo "$subscribe_host" | grep -Eo "\[?[A-Fa-f0-9:]*:[A-Fa-f0-9:]+\]?" | sed "s/^/add $IPSET_VPS6 /" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
echo "$subscribe_host" | grep -Eo "$IPv4_REGEX" | sed "s/^/add $IPSET_VPS /" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
echo "$subscribe_host" | grep -Eo "$IPv6_REGEX" | sed "s/^/add $IPSET_VPS6 /" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
}
}
@@ -779,8 +785,8 @@ filter_server_port() {
local address="$1"
local port=$(echo "$2" | tr '-' ':' | tr -d ' ')
local stream=$(echo "$3" | tr 'A-Z' 'a-z')
local ipt_tmp="$ipt_n" _is_tproxy _ipt_cmd _ver multi_ports p ports
[ "$(config_n_get @global_forwarding[0] tcp_proxy_way redirect)" = "tproxy" ] && _is_tproxy="TPROXY"
local _is_tproxy="$4"
local ipt_tmp="$ipt_n" _ipt_cmd _ver multi_ports p ports
[ "$stream" = "udp" ] && _is_tproxy="TPROXY"
[ -n "$_is_tproxy" ] && ipt_tmp="$ipt_m"
for _ver in 4 6; do
@@ -807,7 +813,7 @@ filter_server_port() {
}
filter_node() {
local node="$1" stream="$2"
local node="$1" stream="$2" _is_tproxy="$3"
[ -z "$node" ] && return 1
local address=$(config_n_get "$node" address)
local port=$(config_n_get "$node" port)
@@ -816,13 +822,15 @@ filter_node() {
[ -z "$address" ] && return 1
echo "$address" | grep -Eq "$EXCLUDE_VPSIP" && return 1
[ -z "$port" ] && return 1
filter_server_port "$address" "$port" "$stream"
filter_server_port "$address" "$port" "$stream" "$_is_tproxy"
}
filter_direct_node_list() {
[ ! -s "$TMP_PATH/direct_node_list" ] && return
local _is_tproxy
[ "$(config_n_get @global_forwarding[0] tcp_proxy_way redirect)" = "tproxy" ] && _is_tproxy="TPROXY"
awk '!seen[$0]++' "$TMP_PATH/direct_node_list" | while read -r _node_id; do
filter_node "$_node_id" TCP
filter_node "$_node_id" TCP "$_is_tproxy"
filter_node "$_node_id" UDP
unset _node_id
done
@@ -921,13 +929,13 @@ add_firewall_rule() {
#直连列表
[ "$USE_DIRECT_LIST_ALL" = "1" ] && {
cat $RULES_PATH/direct_ip | tr -s "\r\n" "\n" | grep -v "^#" | sed -e "/^$/d" | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | sed -e "s/^/add $IPSET_WHITE &/g" -e "s/$/ timeout 0/g" | ipset -! -R
cat $RULES_PATH/direct_ip | tr -s "\r\n" "\n" | grep -v "^#" | sed -e "/^$/d" | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "s/^/add $IPSET_WHITE6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
cat $RULES_PATH/direct_ip | tr -s "\r\n" "\n" | sed 's/#.*//' | grep -E "$IPv4_REGEX" | sed -e "s/^/add $IPSET_WHITE &/g" -e "s/$/ timeout 0/g" | ipset -! -R
cat $RULES_PATH/direct_ip | tr -s "\r\n" "\n" | sed 's/#.*//' | grep -E "$IPv6_REGEX" | sed -e "s/^/add $IPSET_WHITE6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
[ "$USE_GEOVIEW" = "1" ] && {
local GEOIP_CODE=$(cat $RULES_PATH/direct_ip | tr -s "\r\n" "\n" | sed -e "/^$/d" | grep -E "^geoip:" | grep -v "^geoip:private" | sed -E 's/^geoip:(.*)/\1/' | sed ':a;N;$!ba;s/\n/,/g')
if [ -n "$GEOIP_CODE" ]; then
get_geoip $GEOIP_CODE ipv4 | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | sed -e "s/^/add $IPSET_WHITE &/g" -e "s/$/ timeout 0/g" | ipset -! -R
get_geoip $GEOIP_CODE ipv6 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "s/^/add $IPSET_WHITE6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
get_geoip $GEOIP_CODE ipv4 | sed -e "s/^/add $IPSET_WHITE &/g" -e "s/$/ timeout 0/g" | ipset -! -R
get_geoip $GEOIP_CODE ipv6 | sed -e "s/^/add $IPSET_WHITE6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
echolog " - [$?]解析并加入[直连列表] GeoIP 到 IPSET 完成"
fi
}
@@ -935,13 +943,13 @@ add_firewall_rule() {
#代理列表
[ "$USE_PROXY_LIST_ALL" = "1" ] && {
cat $RULES_PATH/proxy_ip | tr -s "\r\n" "\n" | grep -v "^#" | sed -e "/^$/d" | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | sed -e "s/^/add $IPSET_BLACK &/g" -e "s/$/ timeout 0/g" | ipset -! -R
cat $RULES_PATH/proxy_ip | tr -s "\r\n" "\n" | grep -v "^#" | sed -e "/^$/d" | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "s/^/add $IPSET_BLACK6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
cat $RULES_PATH/proxy_ip | tr -s "\r\n" "\n" | sed 's/#.*//' | grep -E "$IPv4_REGEX" | sed -e "s/^/add $IPSET_BLACK &/g" -e "s/$/ timeout 0/g" | ipset -! -R
cat $RULES_PATH/proxy_ip | tr -s "\r\n" "\n" | sed 's/#.*//' | grep -E "$IPv6_REGEX" | sed -e "s/^/add $IPSET_BLACK6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
[ "$USE_GEOVIEW" = "1" ] && {
local GEOIP_CODE=$(cat $RULES_PATH/proxy_ip | tr -s "\r\n" "\n" | sed -e "/^$/d" | grep -E "^geoip:" | grep -v "^geoip:private" | sed -E 's/^geoip:(.*)/\1/' | sed ':a;N;$!ba;s/\n/,/g')
if [ -n "$GEOIP_CODE" ]; then
get_geoip $GEOIP_CODE ipv4 | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | sed -e "s/^/add $IPSET_BLACK &/g" -e "s/$/ timeout 0/g" | ipset -! -R
get_geoip $GEOIP_CODE ipv6 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "s/^/add $IPSET_BLACK6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
get_geoip $GEOIP_CODE ipv4 | sed -e "s/^/add $IPSET_BLACK &/g" -e "s/$/ timeout 0/g" | ipset -! -R
get_geoip $GEOIP_CODE ipv6 | sed -e "s/^/add $IPSET_BLACK6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
echolog " - [$?]解析并加入[代理列表] GeoIP 到 IPSET 完成"
fi
}
@@ -949,13 +957,13 @@ add_firewall_rule() {
#屏蔽列表
[ "$USE_BLOCK_LIST_ALL" = "1" ] && {
cat $RULES_PATH/block_ip | tr -s "\r\n" "\n" | grep -v "^#" | sed -e "/^$/d" | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | sed -e "s/^/add $IPSET_BLOCK &/g" -e "s/$/ timeout 0/g" | ipset -! -R
cat $RULES_PATH/block_ip | tr -s "\r\n" "\n" | grep -v "^#" | sed -e "/^$/d" | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "s/^/add $IPSET_BLOCK6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
cat $RULES_PATH/block_ip | tr -s "\r\n" "\n" | sed 's/#.*//' | grep -E "$IPv4_REGEX" | sed -e "s/^/add $IPSET_BLOCK &/g" -e "s/$/ timeout 0/g" | ipset -! -R
cat $RULES_PATH/block_ip | tr -s "\r\n" "\n" | sed 's/#.*//' | grep -E "$IPv6_REGEX" | sed -e "s/^/add $IPSET_BLOCK6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
[ "$USE_GEOVIEW" = "1" ] && {
local GEOIP_CODE=$(cat $RULES_PATH/block_ip | tr -s "\r\n" "\n" | sed -e "/^$/d" | grep -E "^geoip:" | grep -v "^geoip:private" | sed -E 's/^geoip:(.*)/\1/' | sed ':a;N;$!ba;s/\n/,/g')
if [ -n "$GEOIP_CODE" ]; then
get_geoip $GEOIP_CODE ipv4 | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | sed -e "s/^/add $IPSET_BLOCK &/g" -e "s/$/ timeout 0/g" | ipset -! -R
get_geoip $GEOIP_CODE ipv6 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "s/^/add $IPSET_BLOCK6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
get_geoip $GEOIP_CODE ipv4 | sed -e "s/^/add $IPSET_BLOCK &/g" -e "s/$/ timeout 0/g" | ipset -! -R
get_geoip $GEOIP_CODE ipv6 | sed -e "s/^/add $IPSET_BLOCK6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
echolog " - [$?]解析并加入[屏蔽列表] GeoIP 到 IPSET 完成"
fi
}
@@ -969,16 +977,16 @@ add_firewall_rule() {
local shunt_id
for shunt_id in $shunt_ids; do
[ "${shunt_group}" != "$(config_n_get ${shunt_id} group)" ] && continue
config_n_get $shunt_id ip_list | tr -s "\r\n" "\n" | grep -v "^#" | sed -e "/^$/d" | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | sed -e "s/^/add $IPSET_SHUNT &/g" -e "s/$/ timeout 0/g" | ipset -! -R
config_n_get $shunt_id ip_list | tr -s "\r\n" "\n" | grep -v "^#" | sed -e "/^$/d" | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "s/^/add $IPSET_SHUNT6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
config_n_get $shunt_id ip_list | tr -s "\r\n" "\n" | sed 's/#.*//' | grep -E "$IPv4_REGEX" | sed -e "s/^/add $IPSET_SHUNT &/g" -e "s/$/ timeout 0/g" | ipset -! -R
config_n_get $shunt_id ip_list | tr -s "\r\n" "\n" | sed 's/#.*//' | grep -E "$IPv6_REGEX" | sed -e "s/^/add $IPSET_SHUNT6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
[ "$USE_GEOVIEW" = "1" ] && {
local geoip_code=$(config_n_get $shunt_id ip_list | tr -s "\r\n" "\n" | sed -e "/^$/d" | grep -E "^geoip:" | grep -v "^geoip:private" | sed -E 's/^geoip:(.*)/\1/' | sed ':a;N;$!ba;s/\n/,/g')
[ -n "$geoip_code" ] && GEOIP_CODE="${GEOIP_CODE:+$GEOIP_CODE,}$geoip_code"
}
done
if [ -n "$GEOIP_CODE" ]; then
get_geoip $GEOIP_CODE ipv4 | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | sed -e "s/^/add $IPSET_SHUNT &/g" -e "s/$/ timeout 0/g" | ipset -! -R
get_geoip $GEOIP_CODE ipv6 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "s/^/add $IPSET_SHUNT6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
get_geoip $GEOIP_CODE ipv4 | sed -e "s/^/add $IPSET_SHUNT &/g" -e "s/$/ timeout 0/g" | ipset -! -R
get_geoip $GEOIP_CODE ipv6 | sed -e "s/^/add $IPSET_SHUNT6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
echolog " - [$?]解析并加入[分流节点] GeoIP 到 IPSET 完成"
fi
}
@@ -1090,7 +1098,7 @@ add_firewall_rule() {
insert_rule_before "$ipt_m" "PREROUTING" "mwan3" "-j PSW"
# Only TCP, UDP Invalid.
insert_rule_before "$ipt_m" "PREROUTING" "PSW" "-p tcp -m socket -j PSW_DIVERT"
insert_rule_before "$ipt_m" "PREROUTING" "PSW" "-p tcp -m socket --transparent -j PSW_DIVERT"
$ipt_m -N PSW_OUTPUT
$ipt_m -A PSW_OUTPUT $(dst $IPSET_LAN) -j RETURN
@@ -1164,7 +1172,7 @@ add_firewall_rule() {
insert_rule_before "$ip6t_m" "PREROUTING" "mwan3" "-j PSW"
# Only TCP, UDP Invalid.
insert_rule_before "$ip6t_m" "PREROUTING" "PSW" "-p tcp -m socket -j PSW_DIVERT"
insert_rule_before "$ip6t_m" "PREROUTING" "PSW" "-p tcp -m socket --transparent -j PSW_DIVERT"
$ip6t_m -N PSW_OUTPUT
$ip6t_m -A PSW_OUTPUT -m mark --mark 0xff/0xff -j RETURN
@@ -1469,7 +1477,7 @@ gen_include() {
[ -z "${_ipt}" ] && return
echo "*$2"
${_ipt}-save -t $2 | grep "PSW" | grep -v "\-j PSW$" | grep -v "mangle\-OUTPUT\-PSW" | grep -v "socket \-j PSW_DIVERT$" | sed -e "s/^-A \(OUTPUT\|PREROUTING\)/-I \1 1/"
${_ipt}-save -t $2 | grep "PSW" | grep -v "\-j PSW$" | grep -v "mangle\-OUTPUT\-PSW" | grep -v "\-m socket .*\-j PSW_DIVERT$" | sed -e "s/^-A \(OUTPUT\|PREROUTING\)/-I \1 1/"
echo 'COMMIT'
}
local __ipt=""
@@ -1493,7 +1501,7 @@ gen_include() {
[ -z "${is_tproxy}" ] && \$(${MY_PATH} insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "-p tcp -j PSW")
\$(${MY_PATH} insert_rule_before "$ipt_m" "PREROUTING" "mwan3" "-j PSW")
\$(${MY_PATH} insert_rule_before "$ipt_m" "PREROUTING" "PSW" "-p tcp -m socket -j PSW_DIVERT")
\$(${MY_PATH} insert_rule_before "$ipt_m" "PREROUTING" "PSW" "-p tcp -m socket --transparent -j PSW_DIVERT")
EOF
)
}
@@ -1514,7 +1522,7 @@ gen_include() {
[ "$accept_icmpv6" = "1" ] && $ip6t_n -A PREROUTING -p ipv6-icmp -j PSW
\$(${MY_PATH} insert_rule_before "$ip6t_m" "PREROUTING" "mwan3" "-j PSW")
\$(${MY_PATH} insert_rule_before "$ip6t_m" "PREROUTING" "PSW" "-p tcp -m socket -j PSW_DIVERT")
\$(${MY_PATH} insert_rule_before "$ip6t_m" "PREROUTING" "PSW" "-p tcp -m socket --transparent -j PSW_DIVERT")
EOF
)
}
@@ -296,6 +296,12 @@ load_acl() {
}
[ -n "$(get_cache_var "ACL_${sid}_dns_port")" ] && dns_redirect_port=$(get_cache_var "ACL_${sid}_dns_port")
[ -n "$(get_cache_var "ACL_${sid}_fakedns")" ] && use_fakedns=$(get_cache_var "ACL_${sid}_fakedns")
([ -n "$node" ] && ([ "$node" = "default" ] || [ "$node" = "$NODE" ])) && {
use_global_config=1
unset node
}
[ -n "$node" ] && {
if [ "$(config_get_type $node)" = "socks" ]; then
node_remark="Socks 配置($(config_n_get $node port) 端口)"
@@ -446,16 +452,16 @@ load_acl() {
local shunt_id
for shunt_id in $shunt_ids; do
[ "${shunt_group}" != "$(config_n_get ${shunt_id} group)" ] && continue
config_n_get $shunt_id ip_list | sed 's/#.*//' | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | insert_nftset $shunt_set_name_static
config_n_get $shunt_id ip_list | sed 's/#.*//' | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | insert_nftset $shunt6_set_name_static
config_n_get $shunt_id ip_list | sed 's/#.*//' | grep -E "$IPv4_REGEX" | insert_nftset $shunt_set_name_static
config_n_get $shunt_id ip_list | sed 's/#.*//' | grep -E "$IPv6_REGEX" | insert_nftset $shunt6_set_name_static
[ "$USE_GEOVIEW" = "1" ] && {
local geoip_code=$(config_n_get $shunt_id ip_list | tr -s "\r\n" "\n" | sed -e "/^$/d" | grep -E "^geoip:" | grep -v "^geoip:private" | sed -E 's/^geoip:(.*)/\1/' | sed ':a;N;$!ba;s/\n/,/g')
[ -n "$geoip_code" ] && GEOIP_CODE="${GEOIP_CODE:+$GEOIP_CODE,}$geoip_code"
}
done
if [ -n "$GEOIP_CODE" ]; then
get_geoip $GEOIP_CODE ipv4 | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | insert_nftset $shunt_set_name_static
get_geoip $GEOIP_CODE ipv6 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | insert_nftset $shunt6_set_name_static
get_geoip $GEOIP_CODE ipv4 | insert_nftset $shunt_set_name_static
get_geoip $GEOIP_CODE ipv6 | insert_nftset $shunt6_set_name_static
fi
}
}
@@ -463,16 +469,28 @@ load_acl() {
else
[ -n "${DIRECT_DNSMASQ_PORT}" ] && dns_redirect=${DIRECT_DNSMASQ_PORT}
fi
if [ -n "${dns_redirect}" ]; then
nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol udp ${_ipt_source} udp dport 53 counter return comment \"$remarks\""
[ "$_ipv4" != "1" ] && nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto udp ${_ipt_source} udp dport 53 counter return comment \"$remarks\""
nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol tcp ${_ipt_source} tcp dport 53 counter return comment \"$remarks\""
[ "$_ipv4" != "1" ] && nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto tcp ${_ipt_source} tcp dport 53 counter return comment \"$remarks\""
#nft "add rule $NFTABLE_NAME PSW_DNS ip protocol udp ${_ipt_source} udp dport 53 counter redirect to :${dns_redirect} comment \"$remarks\""
#nft "add rule $NFTABLE_NAME PSW_DNS ip protocol tcp ${_ipt_source} tcp dport 53 counter redirect to :${dns_redirect} comment \"$remarks\""
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto udp ${_ipt_source} udp dport 53 counter redirect to :${dns_redirect} comment \"$remarks\""
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto tcp ${_ipt_source} tcp dport 53 counter redirect to :${dns_redirect} comment \"$remarks\""
[ -z "$(get_cache_var "ACL_${sid}_default")" ] && echolog " - ${msg}使用与全局配置不相同节点,已将DNS强制重定向到专用 DNS 服务器。"
if ([ -n "$tcp_port" ] || [ -n "$udp_port" ]) && [ -n "$dns_redirect" ]; then
if [ "$PROXY_IPV6" = "1" ]; then
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto udp ${_ipt_source} udp dport 53 counter accept"
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto tcp ${_ipt_source} tcp dport 53 counter accept"
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto udp ${_ipt_source} udp dport 53 counter redirect to :$dns_redirect comment \"$remarks\""
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto tcp ${_ipt_source} tcp dport 53 counter redirect to :$dns_redirect comment \"$remarks\""
else
nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol udp ${_ipt_source} udp dport 53 counter accept"
nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol tcp ${_ipt_source} tcp dport 53 counter accept"
nft "add rule $NFTABLE_NAME PSW_DNS ip protocol udp ${_ipt_source} udp dport 53 counter redirect to :$dns_redirect comment \"$remarks\""
nft "add rule $NFTABLE_NAME PSW_DNS ip protocol tcp ${_ipt_source} tcp dport 53 counter redirect to :$dns_redirect comment \"$remarks\""
fi
[ -z "$(get_cache_var "ACL_${sid}_default")" ] && echolog " - ${msg}节点不同于全局配置,DNS 重定向到专用服务器[${dns_redirect}]。"
else
if [ "$PROXY_IPV6" = "1" ]; then
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto udp ${_ipt_source} udp dport 53 counter return comment \"$remarks\""
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto tcp ${_ipt_source} tcp dport 53 counter return comment \"$remarks\""
else
nft "add rule $NFTABLE_NAME PSW_DNS ip protocol udp ${_ipt_source} udp dport 53 counter return comment \"$remarks\""
nft "add rule $NFTABLE_NAME PSW_DNS ip protocol tcp ${_ipt_source} tcp dport 53 counter return comment \"$remarks\""
fi
fi
[ -n "$tcp_port" ] || [ -n "$udp_port" ] && {
@@ -651,15 +669,26 @@ load_acl() {
[ -n "${DIRECT_DNSMASQ_PORT}" ] && DNS_REDIRECT=${DIRECT_DNSMASQ_PORT}
fi
if [ -n "${DNS_REDIRECT}" ]; then
nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol udp udp dport 53 counter return comment \"默认\""
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto udp udp dport 53 counter return comment \"默认\""
nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol tcp tcp dport 53 counter return comment \"默认\""
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto tcp tcp dport 53 counter return comment \"默认\""
nft "add rule $NFTABLE_NAME PSW_DNS ip protocol udp udp dport 53 counter redirect to :${DNS_REDIRECT} comment \"默认\""
nft "add rule $NFTABLE_NAME PSW_DNS ip protocol tcp tcp dport 53 counter redirect to :${DNS_REDIRECT} comment \"默认\""
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto udp udp dport 53 counter redirect to :${DNS_REDIRECT} comment \"默认\""
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto tcp tcp dport 53 counter redirect to :${DNS_REDIRECT} comment \"默认\""
if ([ -n "${TCP_PROXY_MODE}" ] || [ -n "${UDP_PROXY_MODE}" ]) && [ -n "$DNS_REDIRECT" ]; then
if [ "$PROXY_IPV6" = "1" ]; then
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto udp udp dport 53 counter accept"
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 meta l4proto tcp tcp dport 53 counter accept"
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto udp udp dport 53 counter redirect to :$DNS_REDIRECT comment \"默认\""
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto tcp tcp dport 53 counter redirect to :$DNS_REDIRECT comment \"默认\""
else
nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol udp udp dport 53 counter accept"
nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol tcp tcp dport 53 counter accept"
nft "add rule $NFTABLE_NAME PSW_DNS ip protocol udp udp dport 53 counter redirect to :$DNS_REDIRECT comment \"默认\""
nft "add rule $NFTABLE_NAME PSW_DNS ip protocol tcp tcp dport 53 counter redirect to :$DNS_REDIRECT comment \"默认\""
fi
else
if [ "$PROXY_IPV6" = "1" ]; then
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto udp udp dport 53 counter return comment \"默认\""
nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto tcp tcp dport 53 counter return comment \"默认\""
else
nft "add rule $NFTABLE_NAME PSW_DNS ip protocol udp udp dport 53 counter return comment \"默认\""
nft "add rule $NFTABLE_NAME PSW_DNS ip protocol tcp tcp dport 53 counter return comment \"默认\""
fi
fi
[ -n "${TCP_PROXY_MODE}" ] || [ -n "${UDP_PROXY_MODE}" ] && {
@@ -829,12 +858,12 @@ filter_vps_addr() {
filter_vpsip() {
local vps_addrs=$(uci show $CONFIG | grep -E "(\.address=|\.download_address=|\.domain_resolver_dns=|\.domain_resolver_dns_https=)" | cut -d "'" -f 2 | grep -Ev "$EXCLUDE_VPSIP")
local ipv4_addrs=$(echo "$vps_addrs" | grep -Eo "([0-9]{1,3}\.){3}[0-9]{1,3}")
local ipv4_addrs=$(echo "$vps_addrs" | grep -Eo "$IPv4_REGEX")
[ -n "$ipv4_addrs" ] && {
echo "$ipv4_addrs" | insert_nftset $NFTSET_VPS
echolog " - [$?]加入所有IPv4节点服务器IP到nftset[$NFTSET_VPS]直连完成"
}
local ipv6_addrs=$(echo "$vps_addrs" | grep -Eo "\[?[A-Fa-f0-9:]*:[A-Fa-f0-9:]+\]?")
local ipv6_addrs=$(echo "$vps_addrs" | grep -Eo "$IPv6_REGEX")
[ -n "$ipv6_addrs" ] && {
echo "$ipv6_addrs" | insert_nftset $NFTSET_VPS6
echolog " - [$?]加入所有IPv6节点服务器IP到nftset[$NFTSET_VPS6]直连完成"
@@ -842,8 +871,8 @@ filter_vpsip() {
#订阅方式为直连时
local subscribe_host=$(get_subscribe_host | grep -Ev "$EXCLUDE_VPSIP")
[ -n "$subscribe_host" ] && {
echo "$subscribe_host" | grep -Eo "([0-9]{1,3}\.){3}[0-9]{1,3}" | grep -Ev "$EXCLUDE_VPSIP" | insert_nftset $NFTSET_VPS
echo "$subscribe_host" | grep -Eo "\[?[A-Fa-f0-9:]*:[A-Fa-f0-9:]+\]?" | insert_nftset $NFTSET_VPS6
echo "$subscribe_host" | grep -Eo "$IPv4_REGEX" | grep -Ev "$EXCLUDE_VPSIP" | insert_nftset $NFTSET_VPS
echo "$subscribe_host" | grep -Eo "$IPv6_REGEX" | insert_nftset $NFTSET_VPS6
}
}
@@ -851,9 +880,9 @@ filter_server_port() {
local address="$1"
local port=$(echo "$2" | tr ':' '-' | tr -d ' ')
local stream=$(echo "$3" | tr 'A-Z' 'a-z')
local _ip_type _port_expr _ver _is_tproxy
local _is_tproxy="$4"
local _ip_type _port_expr _ver
local _nft_output_chain="PSW_OUTPUT_NAT"
[ "$(config_n_get @global_forwarding[0] tcp_proxy_way redirect)" = "tproxy" ] && _is_tproxy="TPROXY"
[ "$stream" = "udp" ] && _is_tproxy="TPROXY"
[ -n "$_is_tproxy" ] && _nft_output_chain="PSW_OUTPUT_MANGLE"
case "$port" in
@@ -869,7 +898,7 @@ filter_server_port() {
}
filter_node() {
local node="$1" stream="$2"
local node="$1" stream="$2" _is_tproxy="$3"
[ -z "$node" ] && return 1
local address=$(config_n_get "$node" address)
local port=$(config_n_get "$node" port)
@@ -878,13 +907,15 @@ filter_node() {
[ -z "$address" ] && return 1
echo "$address" | grep -Eq "$EXCLUDE_VPSIP" && return 1
[ -z "$port" ] && return 1
filter_server_port "$address" "$port" "$stream"
filter_server_port "$address" "$port" "$stream" "$_is_tproxy"
}
filter_direct_node_list() {
[ ! -s "$TMP_PATH/direct_node_list" ] && return
local _is_tproxy
[ "$(config_n_get @global_forwarding[0] tcp_proxy_way redirect)" = "tproxy" ] && _is_tproxy="TPROXY"
awk '!seen[$0]++' "$TMP_PATH/direct_node_list" | while read -r _node_id; do
filter_node "$_node_id" TCP
filter_node "$_node_id" TCP "$_is_tproxy"
filter_node "$_node_id" UDP
unset _node_id
done
@@ -902,16 +933,22 @@ add_script_mwan3() {
}
}
MWAN3_RULE_ARGS="-m connmark --mark ${FWMARK}/0xffffffff -j RETURN"
mwan3_stop() {
nft list chain ip mangle mwan3_hook >/dev/null 2>&1 || return 0
local handles=$(nft -a list chain ip mangle mwan3_hook 2>/dev/null | grep "${FWMARK}" | awk -F '# handle ' '{print$2}')
for handle in $handles; do
nft delete rule ip mangle mwan3_hook handle ${handle} 2>/dev/null
done
while iptables -w 5 -t mangle -D mwan3_hook ${MWAN3_RULE_ARGS} >/dev/null 2>&1; do :; done
}
mwan3_start() {
nft list chain ip mangle mwan3_hook >/dev/null 2>&1 || return 0
mwan3_stop
nft list chain ip mangle mwan3_hook >/dev/null 2>&1 && nft insert rule ip mangle mwan3_hook ct mark ${FWMARK} counter return >/dev/null 2>&1
iptables -w 5 -t mangle -I mwan3_hook 1 ${MWAN3_RULE_ARGS} >/dev/null 2>&1 || \
logger -t passwall "mwan3: failed to add ${FWMARK} exemption rule to mangle/mwan3_hook"
}
update_wan_sets() {
@@ -1024,13 +1061,13 @@ add_firewall_rule() {
#直连列表
[ "$USE_DIRECT_LIST_ALL" = "1" ] && {
cat $RULES_PATH/direct_ip | sed 's/#.*//' | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | insert_nftset $NFTSET_WHITE_STATIC
cat $RULES_PATH/direct_ip | sed 's/#.*//' | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | insert_nftset $NFTSET_WHITE6_STATIC
cat $RULES_PATH/direct_ip | sed 's/#.*//' | grep -E "$IPv4_REGEX" | insert_nftset $NFTSET_WHITE_STATIC
cat $RULES_PATH/direct_ip | sed 's/#.*//' | grep -E "$IPv6_REGEX" | insert_nftset $NFTSET_WHITE6_STATIC
[ "$USE_GEOVIEW" = "1" ] && {
local GEOIP_CODE=$(cat $RULES_PATH/direct_ip | tr -s "\r\n" "\n" | sed -e "/^$/d" | grep -E "^geoip:" | grep -v "^geoip:private" | sed -E 's/^geoip:(.*)/\1/' | sed ':a;N;$!ba;s/\n/,/g')
if [ -n "$GEOIP_CODE" ]; then
get_geoip $GEOIP_CODE ipv4 | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | insert_nftset $NFTSET_WHITE_STATIC
get_geoip $GEOIP_CODE ipv6 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | insert_nftset $NFTSET_WHITE6_STATIC
get_geoip $GEOIP_CODE ipv4 | insert_nftset $NFTSET_WHITE_STATIC
get_geoip $GEOIP_CODE ipv6 | insert_nftset $NFTSET_WHITE6_STATIC
echolog " - [$?]解析并加入[直连列表] GeoIP 到 NFTSET 完成"
fi
}
@@ -1038,13 +1075,13 @@ add_firewall_rule() {
#代理列表
[ "$USE_PROXY_LIST_ALL" = "1" ] && {
cat $RULES_PATH/proxy_ip | sed 's/#.*//' | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | insert_nftset $NFTSET_BLACK_STATIC
cat $RULES_PATH/proxy_ip | sed 's/#.*//' | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | insert_nftset $NFTSET_BLACK6_STATIC
cat $RULES_PATH/proxy_ip | sed 's/#.*//' | grep -E "$IPv4_REGEX" | insert_nftset $NFTSET_BLACK_STATIC
cat $RULES_PATH/proxy_ip | sed 's/#.*//' | grep -E "$IPv6_REGEX" | insert_nftset $NFTSET_BLACK6_STATIC
[ "$USE_GEOVIEW" = "1" ] && {
local GEOIP_CODE=$(cat $RULES_PATH/proxy_ip | tr -s "\r\n" "\n" | sed -e "/^$/d" | grep -E "^geoip:" | grep -v "^geoip:private" | sed -E 's/^geoip:(.*)/\1/' | sed ':a;N;$!ba;s/\n/,/g')
if [ -n "$GEOIP_CODE" ]; then
get_geoip $GEOIP_CODE ipv4 | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | insert_nftset $NFTSET_BLACK_STATIC
get_geoip $GEOIP_CODE ipv6 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | insert_nftset $NFTSET_BLACK6_STATIC
get_geoip $GEOIP_CODE ipv4 | insert_nftset $NFTSET_BLACK_STATIC
get_geoip $GEOIP_CODE ipv6 | insert_nftset $NFTSET_BLACK6_STATIC
echolog " - [$?]解析并加入[代理列表] GeoIP 到 NFTSET 完成"
fi
}
@@ -1052,13 +1089,13 @@ add_firewall_rule() {
#屏蔽列表
[ "$USE_BLOCK_LIST_ALL" = "1" ] && {
cat $RULES_PATH/block_ip | sed 's/#.*//' | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | insert_nftset $NFTSET_BLOCK_STATIC
cat $RULES_PATH/block_ip | sed 's/#.*//' | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | insert_nftset $NFTSET_BLOCK6_STATIC
cat $RULES_PATH/block_ip | sed 's/#.*//' | grep -E "$IPv4_REGEX" | insert_nftset $NFTSET_BLOCK_STATIC
cat $RULES_PATH/block_ip | sed 's/#.*//' | grep -E "$IPv6_REGEX" | insert_nftset $NFTSET_BLOCK6_STATIC
[ "$USE_GEOVIEW" = "1" ] && {
local GEOIP_CODE=$(cat $RULES_PATH/block_ip | tr -s "\r\n" "\n" | sed -e "/^$/d" | grep -E "^geoip:" | grep -v "^geoip:private" | sed -E 's/^geoip:(.*)/\1/' | sed ':a;N;$!ba;s/\n/,/g')
if [ -n "$GEOIP_CODE" ]; then
get_geoip $GEOIP_CODE ipv4 | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | insert_nftset $NFTSET_BLOCK_STATIC
get_geoip $GEOIP_CODE ipv6 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | insert_nftset $NFTSET_BLOCK6_STATIC
get_geoip $GEOIP_CODE ipv4 | insert_nftset $NFTSET_BLOCK_STATIC
get_geoip $GEOIP_CODE ipv6 | insert_nftset $NFTSET_BLOCK6_STATIC
echolog " - [$?]解析并加入[屏蔽列表] GeoIP 到 NFTSET 完成"
fi
}
@@ -1072,16 +1109,16 @@ add_firewall_rule() {
local shunt_id
for shunt_id in $shunt_ids; do
[ "${shunt_group}" != "$(config_n_get ${shunt_id} group)" ] && continue
config_n_get $shunt_id ip_list | sed 's/#.*//' | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | insert_nftset $NFTSET_SHUNT_STATIC
config_n_get $shunt_id ip_list | sed 's/#.*//' | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | insert_nftset $NFTSET_SHUNT6_STATIC
config_n_get $shunt_id ip_list | sed 's/#.*//' | grep -E "$IPv4_REGEX" | insert_nftset $NFTSET_SHUNT_STATIC
config_n_get $shunt_id ip_list | sed 's/#.*//' | grep -E "$IPv6_REGEX" | insert_nftset $NFTSET_SHUNT6_STATIC
[ "$USE_GEOVIEW" = "1" ] && {
local geoip_code=$(config_n_get $shunt_id ip_list | tr -s "\r\n" "\n" | sed -e "/^$/d" | grep -E "^geoip:" | grep -v "^geoip:private" | sed -E 's/^geoip:(.*)/\1/' | sed ':a;N;$!ba;s/\n/,/g')
[ -n "$geoip_code" ] && GEOIP_CODE="${GEOIP_CODE:+$GEOIP_CODE,}$geoip_code"
}
done
if [ -n "$GEOIP_CODE" ]; then
get_geoip $GEOIP_CODE ipv4 | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | insert_nftset $NFTSET_SHUNT_STATIC
get_geoip $GEOIP_CODE ipv6 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | insert_nftset $NFTSET_SHUNT6_STATIC
get_geoip $GEOIP_CODE ipv4 | insert_nftset $NFTSET_SHUNT_STATIC
get_geoip $GEOIP_CODE ipv6 | insert_nftset $NFTSET_SHUNT6_STATIC
echolog " - [$?]解析并加入[分流节点] GeoIP 到 NFTSET 完成"
fi
}
@@ -1296,10 +1333,15 @@ add_firewall_rule() {
if [ -n "$NODE" ] && ([ -n "${LOCALHOST_TCP_PROXY_MODE}" ] || [ -n "${LOCALHOST_UDP_PROXY_MODE}" ]); then
[ -n "$DNS_REDIRECT_PORT" ] && {
nft "add rule $NFTABLE_NAME nat_output ip protocol udp oif lo udp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"PSW_DNS\""
nft "add rule $NFTABLE_NAME nat_output ip protocol tcp oif lo tcp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"PSW_DNS\""
nft "add rule $NFTABLE_NAME nat_output meta l4proto udp oif lo udp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"PSW_DNS\""
nft "add rule $NFTABLE_NAME nat_output meta l4proto tcp oif lo tcp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"PSW_DNS\""
if [ "$PROXY_IPV6" == "1" ]; then
#nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 meta l4proto udp udp dport 53 counter accept"
#nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 meta l4proto tcp tcp dport 53 counter accept"
nft "add rule $NFTABLE_NAME nat_output oif lo meta l4proto udp udp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"PSW_DNS\""
nft "add rule $NFTABLE_NAME nat_output oif lo meta l4proto tcp tcp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"PSW_DNS\""
else
nft "add rule $NFTABLE_NAME nat_output oif lo ip protocol udp udp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"PSW_DNS\""
nft "add rule $NFTABLE_NAME nat_output oif lo ip protocol tcp tcp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"PSW_DNS\""
fi
}
fi
@@ -183,8 +183,6 @@ test_auto_switch() {
start() {
id=$1
LOCK_FILE=${LOCK_PATH}/${CONFIG}_socks_auto_switch_${id}.lock
LOG_EVENT_FILTER=$(uci -q get "${CONFIG}.global[0].log_event_filter" 2>/dev/null)
LOG_EVENT_CMD=$(uci -q get "${CONFIG}.global[0].log_event_cmd" 2>/dev/null)
main_node=$(config_n_get $id node)
socks_port=$(config_n_get $id port 0)
delay=$(config_n_get $id autoswitch_testing_time 30)
@@ -40,10 +40,11 @@ do
mkdir -p $TMP_SUB_PATH
for item in $(uci show ${CONFIG} | grep "=subscribe_list" | cut -d '.' -sf 2 | cut -d '=' -sf 1); do
sub_update_week_mode=$(config_n_get $item update_week_mode)
if [ -n "$sub_update_week_mode" ]; then
remark=$(config_n_get $item remark)
if [ -n "$sub_update_week_mode" ] && [ "$sub_update_week_mode" = "8" ]; then
sub_update_interval_mode=$(config_n_get $item update_interval_mode)
echo "$item" >> $TMP_SUB_PATH/${sub_update_week_mode}_${sub_update_interval_mode}
[ -n "$sub_update_interval_mode" ] && {
echo "$item" >> $TMP_SUB_PATH/${sub_update_week_mode}_${sub_update_interval_mode}
}
fi
done
@@ -56,7 +57,6 @@ do
[ "$sub_update_week_mode" = "8" ] && {
[ "$(expr "$CFG_UPDATE_INT" % "$sub_update_interval_mode")" -eq 0 ] && { lua $APP_PATH/subscribe.lua start $cfgids cron > /dev/null 2>&1 & }
}
done
rm -rf $TMP_SUB_PATH
}
+100 -104
View File
@@ -15,6 +15,20 @@ TMP_ROUTE_PATH=${TMP_PATH}/route
TMP_SCRIPT_FUNC_PATH=${TMP_PATH}/script_func
RULES_PATH=/usr/share/${CONFIG}/rules
IPv6_REGEX="([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|"
IPv6_REGEX="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,7}:|"
IPv6_REGEX="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|"
IPv6_REGEX="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|"
IPv6_REGEX="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|"
IPv6_REGEX="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|"
IPv6_REGEX="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|"
IPv6_REGEX="${IPv6_REGEX}[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|"
IPv6_REGEX="${IPv6_REGEX}:((:[0-9a-fA-F]{1,4}){1,7}|:)|"
IPv6_REGEX="${IPv6_REGEX}fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|"
IPv6_REGEX="${IPv6_REGEX}::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|"
IPv6_REGEX="${IPv6_REGEX}([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])"
IPv4_REGEX="((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
. /lib/functions/network.sh
echolog() {
@@ -46,6 +60,63 @@ config_t_get() {
echo "${ret:=${3}}"
}
eval_set_val() {
for i in $@; do
for j in $i; do
eval $j
done
done
}
eval_unset_val() {
for i in $@; do
for j in $i; do
eval unset $j
done
done
}
lua_api() {
local func=${1}
[ -z "${func}" ] && {
echo "nil"
return
}
echo $(lua -e "local api = require 'luci.passwall.api' print(api.${func})")
}
eval_cache_var() {
[ -s "$TMP_PATH/var" ] && eval $(cat "$TMP_PATH/var")
}
del_cache_var() {
local key="${1}"
[ -n "${key}" ] && [ -f "${TMP_PATH}/var" ] && {
sed -i "/${key}=/d" $TMP_PATH/var >/dev/null 2>&1
}
}
set_cache_var() {
local key="${1}"
shift 1
[ -n "${key}" ] && {
del_cache_var ${key}
local val="$@"
[ -n "${val}" ] && {
[ ! -d $TMP_PATH ] && mkdir -p $TMP_PATH
echo "${key}=\"${val}\"" >> $TMP_PATH/var
eval ${key}=\"${val}\"
}
}
}
get_cache_var() {
local key="${1}"
[ -n "${key}" ] && [ -s "$TMP_PATH/var" ] && {
echo $(cat $TMP_PATH/var | grep "^${key}=" | awk -F '=' '{print $2}' | tail -n 1 | awk -F'"' '{print $2}')
}
}
first_type() {
[ "${1#/}" != "$1" ] && [ -x "$1" ] && echo "$1" && return
for p in "/bin/$1" "/usr/bin/$1" "${TMP_BIN_PATH:-/tmp}/$1"; do
@@ -85,12 +156,12 @@ get_host_ip() {
local isip=""
local ip=""
if [ "$1" = "ipv6" ]; then
isip=$(echo $host | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}")
isip=$(echo $host | grep -Eo "$IPv6_REGEX")
if [ -n "$isip" ]; then
ip=$(echo "$host" | tr -d '[]')
fi
else
isip=$(echo $host | grep -E "([0-9]{1,3}[\.]){3}[0-9]{1,3}")
isip=$(echo $host | grep -Eo "$IPv4_REGEX")
[ -n "$isip" ] && ip=$isip
fi
[ -z "$isip" ] && {
@@ -269,53 +340,42 @@ get_new_port() {
local default_start_port=2001
local min_port=1025
local max_port=49151
local port="$1"
local port="$1" # Required parameter; please pass "auto" if you want it to be automatic.
local protocol=$(echo "$2" | tr 'A-Z' 'a-z')
local LOCK_FILE="${LOCK_PATH}/${CONFIG}_get_prot.lock"
while ! mkdir "$LOCK_FILE" 2>/dev/null; do
sleep 0.05
done
local is_auto
if [ "$port" = "auto" ]; then
local now last_time diff last_port
now=$(date +%s 2>/dev/null)
last_time=$(get_cache_var "last_get_new_port_time")
if [ -n "$now" ] && [ -n "$last_time" ]; then
diff=$(expr "$now" - "$last_time")
[ "$diff" -lt 0 ] && diff=$(expr 0 - "$diff")
is_auto=1
local last_get_new_port_auto=$(get_cache_var "last_get_new_port_auto")
if [ -n "$last_get_new_port_auto" ]; then
port=$(expr "$last_get_new_port_auto" + 1)
else
diff=999
fi
if [ "$diff" -gt 10 ]; then
port=$default_start_port
else
last_port=$(get_cache_var "last_get_new_port_auto")
if [ -n "$last_port" ]; then
port=$(expr "$last_port" + 1)
else
port=$default_start_port
fi
fi
fi
[ "$port" -lt $min_port ] || [ "$port" -gt $max_port ] && port=$default_start_port
local start_port="$port"
([ "$port" -lt "$min_port" ] || [ "$port" -gt "$max_port" ]) && port=$default_start_port
while :; do
if [ "$(check_port_exists "$port" "$protocol")" = 0 ]; then
break
local result=$(check_port_exists "$port" "$protocol")
if [ "$is_auto" = "1" ] && [ -n "$(get_cache_var "get_port_${port}")" ]; then
# The port has already been allocated, continue to the next port.
result=1
fi
port=$(expr "$port" + 1)
if [ "$port" -gt $max_port ]; then
port=$min_port
[ "$result" = "0" ] && break
if [ "$port" -lt "$max_port" ]; then
# If the port is smaller than the maximum port, increment by 1 and continue.
port=$(expr "$port" + 1)
elif [ "$port" -gt "$min_port" ]; then
# If the port is greater than the minimum port, decrement by 1 and continue.
port=$(expr "$port" - 1)
else
# Otherwise, reassign the default starting port.
port=$default_start_port
fi
[ "$port" = "$start_port" ] && {
rmdir "$LOCK_FILE" 2>/dev/null
return 1
}
done
if [ "$1" = "auto" ]; then
if [ "$is_auto" = "1" ]; then
# Set cache to prevent the port from being allocated again.
set_cache_var "get_port_${port}" "1"
set_cache_var "last_get_new_port_auto" "$port"
[ -n "$now" ] && set_cache_var "last_get_new_port_time" "$now"
fi
rmdir "$LOCK_FILE" 2>/dev/null
echo "$port"
}
@@ -345,54 +405,6 @@ check_ver() {
echo 255
}
eval_set_val() {
for i in $@; do
for j in $i; do
eval $j
done
done
}
eval_unset_val() {
for i in $@; do
for j in $i; do
eval unset $j
done
done
}
lua_api() {
local func=${1}
[ -z "${func}" ] && {
echo "nil"
return
}
echo $(lua -e "local api = require 'luci.passwall.api' print(api.${func})")
}
set_cache_var() {
local key="${1}"
shift 1
local val="$@"
[ -n "${key}" ] && [ -n "${val}" ] && {
[ ! -d $TMP_PATH ] && mkdir -p $TMP_PATH
sed -i "/${key}=/d" $TMP_PATH/var >/dev/null 2>&1
echo "${key}=\"${val}\"" >> $TMP_PATH/var
eval ${key}=\"${val}\"
}
}
get_cache_var() {
local key="${1}"
[ -n "${key}" ] && [ -s "$TMP_PATH/var" ] && {
echo $(cat $TMP_PATH/var | grep "^${key}=" | awk -F '=' '{print $2}' | tail -n 1 | awk -F'"' '{print $2}')
}
}
eval_cache_var() {
[ -s "$TMP_PATH/var" ] && eval $(cat "$TMP_PATH/var")
}
has_1_65535() {
local val="$1"
val=${val//:/-}
@@ -456,25 +468,9 @@ ln_run() {
echolog " - 找不到 ${ln_name},无法启动..."
return 1
}
[ "${output}" != "/dev/null" ] && [ -n "$(echo "${output}" | grep -E "default|socks_")" ] && [ "${ln_name}" != "chinadns-ng" ] && {
local persist_log_path=$(config_n_get @global[0] persist_log_path)
local sys_log=$(config_n_get @global[0] sys_log "0")
}
if [ -z "$persist_log_path" ] && [ "$sys_log" != "1" ]; then
${file_func:-echolog " - ${ln_name}"} "$@" >${output} 2>&1 &
else
if [ -n "${persist_log_path}" ]; then
mkdir -p ${persist_log_path}
local log_file=${persist_log_path}/passwall_global_${ln_name}_$(date '+%F').log
echolog "记录到持久性日志文件:${log_file}"
${file_func:-echolog " - ${ln_name}"} "$@" >> ${log_file} 2>&1 &
sys_log=0
fi
if [ "${sys_log}" = "1" ]; then
echolog "记录 ${ln_name}_global 到系统日志"
${file_func:-echolog " - ${ln_name}"} "$@" 2>&1 | logger -t PASSWALL_global_${ln_name} &
fi
fi
${file_func:-echolog " - ${ln_name}"} "$@" >${output} 2>&1 &
[ "$NO_REC_PROCESS" = "1" ] && return
process_count=$(ls $TMP_SCRIPT_FUNC_PATH | wc -l)
process_count=$((process_count + 1))
+2 -2
View File
@@ -6,8 +6,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall2
PKG_VERSION:=26.9.2
PKG_RELEASE:=1
PKG_VERSION:=26.9.12
PKG_RELEASE:=2
PKG_PO_VERSION:=$(PKG_VERSION)
PKG_CONFIG_DEPENDS:= \
@@ -61,14 +61,11 @@ function getOption(config, section, opt) {
const id = `cbid.${config}.${section}.${opt}`;
obj = document.getElementsByName(id)[0] || document.getElementById(id);
if (obj) {
//lua luci combobox
const combobox = document.getElementById('cbi.combobox.' + id);
if (combobox) {
obj.combobox = combobox;
}
const div = document.getElementById(id);
if (div && div.getElementsByTagName("li").length > 0) {
obj = div;
}
return obj;
} else {
return null;
@@ -238,7 +238,7 @@ end
function get_now_use_node()
local e = {}
local node = api.get_cache_var("ACL_GLOBAL_node")
local node = api.get_cache_var(("ACL_${flag}_node"):gsub("${flag}", "acl_default"))
if node then
e["global"] = node
end
@@ -280,7 +280,7 @@ end
function index_status()
local e = {}
e["global_status"] = luci.sys.call("/bin/busybox top -bn1 | grep -v 'grep' | grep '%s/bin/' | grep '/acl/default' >/dev/null" % api.TMP_PATH) == 0
e["global_status"] = luci.sys.call("/bin/busybox top -bn1 | grep -v 'grep' | grep '%s/bin/' | grep '/acl_default\\.json' >/dev/null" % api.TMP_PATH) == 0
http_write_json(e)
end
@@ -387,7 +387,15 @@ function add_node()
uci_set(uid, "group", group)
end
uci_set(uid, "type", "Xray")
if api.finded_com("sing-box") then
uci_set(uid, "type", "sing-box")
elseif api.finded_com("xray") then
uci_set(uid, "type", "Xray")
elseif api.is_finded("sslocal") then
uci_set(uid, "type", "SS-Rust")
elseif api.is_finded("ssr-local") then
uci_set(uid, "type", "SSR")
end
if redirect == "1" then
uci_save()
@@ -43,20 +43,21 @@ end
-- [[ ACLs Settings ]]--
s = m:section(NamedSection, arg[1], translate("ACLs"), translate("ACLs"))
s.addremove = false
s.dynamic = false
---- Enable
o = s:option(Flag, "enabled", translate("Enable"))
s:tab("Main", translate("Main"))
s:tab("Proxy", translate("Proxy"))
s:tab("DNS", translate("DNS"))
s:tab("Log", translate("Log"))
o = s:taboption("Main", Flag, "enabled", translate("Enable"))
o.default = 1
o.rmempty = false
---- Remarks
o = s:option(Value, "remarks", translate("Remarks"))
o = s:taboption("Main", Value, "remarks", translate("Remarks"))
o.default = arg[1]
o.rmempty = false
o = s:option(Value, "interface", translate("Source Interface"))
o = s:taboption("Main", Value, "interface", translate("Source Interface"))
o:value("", translate("All"))
local iface = api.get_network_devices()
for _, d in ipairs(iface) do
@@ -90,7 +91,7 @@ table.sort(mac_t, function(a,b)
end)
---- Source
sources = s:option(DynamicList, "sources", translate("Source"))
sources = s:taboption("Main", DynamicList, "sources", translate("Source"))
sources.description = "<ul><li>" .. translate("Example:")
.. "</li><li>" .. translate("MAC") .. ": 00:00:00:FF:FF:FF"
.. "</li><li>" .. translate("IP") .. ": 192.168.1.100"
@@ -140,36 +141,32 @@ sources.validate = function(self, value, t)
end
local NODE = m:get("@global[0]", "node")
o = s:option(ListValue, "mode", translate("Mode"))
o = s:taboption("Main", ListValue, "mode", translate("Mode"))
o:value("0", translate("No Proxy"))
o:value("1", translate("Proxy"))
o:value("2", translate("Proxy") .. " " .. translate("Use global config") .. "(" .. api.get_node_name(NODE) .. ")")
o = s:option(Value, "tcp_no_redir_ports", translate("TCP No Redir Ports"))
o = s:taboption("Proxy", Value, "tcp_no_redir_ports", translate("TCP No Redir Ports"))
o:value("", translate("No patterns are used"))
o:value("1:65535", translate("All"))
o:depends("mode", "1")
o:depends("mode", "2")
o.validate = port_validate
o = s:option(Value, "udp_no_redir_ports", translate("UDP No Redir Ports"))
o = s:taboption("Proxy", Value, "udp_no_redir_ports", translate("UDP No Redir Ports"))
o:value("", translate("No patterns are used"))
o:value("1:65535", translate("All"))
o:depends("mode", "1")
o:depends("mode", "2")
o.validate = port_validate
o = s:option(DummyValue, "_show_tcp_redir", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o = s:taboption("Main", HideValue, "_show_tcp_redir")
o:depends({ tcp_no_redir_ports = "1:65535", ['!reverse'] = true })
o = s:option(DummyValue, "_show_udp_redir", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o = s:taboption("Main", HideValue, "_show_udp_redir")
o:depends({ udp_no_redir_ports = "1:65535", ['!reverse'] = true })
o = s:option(Value, "tcp_redir_ports", translate("TCP Redir Ports"))
o = s:taboption("Proxy", Value, "tcp_redir_ports", translate("TCP Redir Ports"))
o:value("1:65535", translate("All"))
o:value("22,25,53,80,143,443,465,587,853,873,993,995,5222,8080,8443,9418", translate("Common Use"))
o:value("80,443", "80,443")
@@ -178,14 +175,14 @@ o.validate = port_validate
o:depends({ _node = "1", _show_tcp_redir = "1" })
o:depends({ mode = "2", _show_tcp_redir = "1" })
o = s:option(Value, "udp_redir_ports", translate("UDP Redir Ports"))
o = s:taboption("Proxy", Value, "udp_redir_ports", translate("UDP Redir Ports"))
o:value("1:65535", translate("All"))
o.default = o.keylist[1]
o.validate = port_validate
o:depends({ _node = "1", _show_udp_redir = "1" })
o:depends({ mode = "2", _show_udp_redir = "1" })
o = s:option(DummyValue, "tips", " ")
o = s:taboption("Proxy", DummyValue, "tips", " ")
o.rawhtml = true
o.cfgvalue = function(t, n)
return string.format('<font color="red">%s</font>',
@@ -194,47 +191,42 @@ end
o:depends("mode", "1")
o:depends("mode", "2")
o = s:option(DummyValue, "_no_proxy", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o = s:taboption("Main", HideValue, "_no_proxy")
o:depends("mode", "0")
o:depends({ tcp_no_redir_ports = "1:65535", udp_no_redir_ports = "1:65535" })
o = s:option(DummyValue, "_show_node_option", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o = s:taboption("Main", HideValue, "_show_node_option")
o:depends({ mode = "1", _no_proxy = "" })
o = s:option(ListValue, "node", "<a style='color: red'>" .. translate("Node") .. "</a>")
o = s:taboption("Main", ListValue, "node", "<a style='color: red'>" .. translate("Node") .. "</a>")
o.group = {}
o:depends("_show_node_option", "1")
o.template = m:template_path("/cbi/nodes_listvalue")
current_node_id = o:formvalue(arg[1])
if not current_node_id then
current_node_id = m:get(arg[1], "node")
current_node_id = m:get(arg[1], "node")
local node_value = s.fields["node"]:formvalue(s.section)
if node_value then
current_node_id = node_value
end
current_node = current_node_id and m:get(current_node_id) or {}
o = s:option(DummyValue, "_node", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o = s:taboption("Main", HideValue, "node_save_before")
o.value = current_node[".name"]
o.cbid = function(self, section) return "node_save_before" end
o = s:taboption("Main", HideValue, "_node")
o:depends({ node = "", ['!reverse'] = true })
o = s:option(DummyValue, "_is_singbox", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o = s:taboption("Main", HideValue, "_is_singbox")
o:depends("_hide", "1")
o = s:option(DummyValue, "_is_xray", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o = s:taboption("Main", HideValue, "_is_xray")
o:depends("_hide", "1")
o = s:option(Flag, "log", translate("Enable Node Log"))
o = s:taboption("Log", Flag, "log", translate("Enable Node Log"))
o:depends("_node", "1")
o = s:option(ListValue, "loglevel", translate("Log Level"))
o = s:taboption("Log", ListValue, "loglevel", translate("Log Level"))
o.default = "warn"
o:value("debug", "Debug")
o:value("info", "Info")
@@ -242,33 +234,35 @@ o:value("warn", "Warning")
o:value("error", "Error")
o:depends("log", "1")
o = s:option(DummyValue, "_acl_log", translate("Log File"))
o = s:taboption("Log", DummyValue, "_acl_log", translate("Log File"))
o.rawhtml = true
o.cfgvalue = function(t, n)
local log_path = api.TMP_PATH .. "/acl/" .. arg[1] .. ".log"
local log_file = api.TMP_PATH .. "/acl/" .. arg[1] .. ".log"
local log_url = api.url("get_redir_log") .. "?id=" .. arg[1]
return string.format(
'<code>%s</code>&nbsp;&nbsp;<input class="btn cbi-button cbi-button-apply" type="button" value="%s" onclick="window.open(\'%s\', \'_blank\')" />',
log_path,
translate("View Log"),
log_url
)
local s = "<code>%s</code>&nbsp;&nbsp;" % log_file
if api.fs.access(log_file) then
local btn = string.format(
'<input class="btn cbi-button cbi-button-apply" type="button" value="%s" onclick="window.open(\'%s\', \'_blank\')" />',
translate("View Log"),
log_url
)
s = s .. btn
end
return s
end
o:depends("log", "1")
o = s:option(DummyValue, "_show_dns_option", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o = s:taboption("DNS", HideValue, "_show_dns_option")
o:depends({ mode = "1", _node = "1" })
o = s:option(ListValue, "direct_dns_query_strategy", translate("Direct Query Strategy"))
o = s:taboption("DNS", ListValue, "direct_dns_query_strategy", translate("Direct Query Strategy"))
o.default = "UseIP"
o:value("UseIP")
o:value("UseIPv4")
o:value("UseIPv6")
o:depends("_show_dns_option", "1")
o = s:option(ListValue, "remote_dns_protocol", translate("Remote DNS Protocol"))
o = s:taboption("DNS", ListValue, "remote_dns_protocol", translate("Remote DNS Protocol"))
o:value("tcp", "TCP")
o:value("doh", "DoH")
o:value("udp", "UDP")
@@ -286,7 +280,7 @@ end
o:depends("_show_dns_option", "1")
---- DNS over TCP or UDP or TLS (DoT) or QUIC (DoQ)
o = s:option(Value, "remote_dns", translate("Remote DNS"))
o = s:taboption("DNS", Value, "remote_dns", translate("Remote DNS"))
o.datatype = "or(ipaddr,ipaddrport(1))"
o.default = "1.1.1.1"
o:value("1.1.1.1", "1.1.1.1 (CloudFlare)")
@@ -303,7 +297,7 @@ o:depends("remote_dns_protocol", "quic")
o:depends("remote_dns_protocol", "tls")
---- DNS over HTTP (DoH) or DNS over HTTP3(DoH3)
o = s:option(Value, "remote_dns_doh", translate("Remote DNS DoH"))
o = s:taboption("DNS", Value, "remote_dns_doh", translate("Remote DNS DoH"))
o:value("https://1.1.1.1/dns-query", "CloudFlare")
o:value("https://1.1.1.2/dns-query", "CloudFlare-Security")
o:value("https://8.8.4.4/dns-query", "Google 8844")
@@ -319,42 +313,42 @@ o.validate = doh_validate
o:depends("remote_dns_protocol", "doh")
o:depends("remote_dns_protocol", "http3")
o = s:option(Value, "remote_dns_client_ip", translate("Remote DNS EDNS Client Subnet"))
o = s:taboption("DNS", Value, "remote_dns_client_ip", translate("Remote DNS EDNS Client Subnet"))
o.description = translate("Notify the DNS server when the DNS query is notified, the location of the client (cannot be a private IP address).") .. "<br />" ..
translate("This feature requires the DNS server to support the Edns Client Subnet (RFC7871).")
o.datatype = "ipaddr"
o:depends("_show_dns_option", "1")
o = s:option(ListValue, "remote_dns_detour", translate("Remote DNS Outbound"))
o = s:taboption("DNS", ListValue, "remote_dns_detour", translate("Remote DNS Outbound"))
o.default = "remote"
o:value("remote", translate("Remote"))
o:value("direct", translate("Direct"))
o:depends("_show_dns_option", "1")
o = s:option(Flag, "remote_fakedns", "FakeDNS", translate("Use FakeDNS work in the domain that proxy."))
o = s:taboption("DNS", Flag, "remote_fakedns", "FakeDNS", translate("Use FakeDNS work in the domain that proxy."))
o.default = "0"
o.rmempty = false
o:depends("_hide", "1")
o = s:option(ListValue, "remote_dns_query_strategy", translate("Remote Query Strategy"))
o = s:taboption("DNS", ListValue, "remote_dns_query_strategy", translate("Remote Query Strategy"))
o.default = "UseIPv4"
o:value("UseIP")
o:value("UseIPv4")
o:value("UseIPv6")
o:depends("_show_dns_option", "1")
o = s:option(Value, "remote_rewrite_ttl", translate("Remote DNS") .. " TTL")
o = s:taboption("DNS", Value, "remote_rewrite_ttl", translate("Remote DNS") .. " TTL")
o.datatype = "min(1)"
o.default = "30"
o:depends({ _show_dns_option = "1", _is_singbox = "1" })
o = s:option(ListValue, "dns_hosts_mode", translate("Domain Override"))
o = s:taboption("DNS", ListValue, "dns_hosts_mode", translate("Domain Override"))
o:value("default", translate("Use global config"))
o:value("disable", translate("No patterns are used"))
o:value("custom", translate("-- custom --"))
o:depends("_show_dns_option", "1")
o = s:option(TextValue, "dns_hosts", translate("Domain Override"))
o = s:taboption("DNS", TextValue, "dns_hosts", translate("Domain Override"))
o.rows = 5
o.wrap = "off"
o:depends("dns_hosts_mode", "custom")
@@ -390,19 +384,16 @@ for k, v in pairs(nodes_table) do
end
end
--[[
-- Shunt
if current_node.protocol == "_shunt" then
local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall2/client/include/shunt_options.lua")
setfenv(shunt_lua, getfenv(1))(m, s, {
s_cfgid = s.section,
node_id = current_node_id,
node = current_node,
verify_option = s.fields["node"]
verify_option = s.fields["node"],
tab = "Shunt",
tab_desc = translate("Shunt Rule"),
})
end
m:appendTemplate("/acl/shunt", { shunt_list = api.jsonc.stringify(shunt_list), section = s.section })
]]--
m:appendTemplate("/include/node_change", { verify_option = s.fields["node"], shunt_list = api.jsonc.stringify(shunt_list) })
return api.return_map(m)
@@ -85,36 +85,33 @@ o.template = m:template_path("/cbi/nodes_listvalue")
o:value("", translate("Close"))
o.group = {""}
o = s:taboption("Main", DummyValue, "_node", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o = s:taboption("Main", HideValue, "_node")
o:depends({ node = "", ['!reverse'] = true })
o = s:taboption("Main", DummyValue, "_is_singbox", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o = s:taboption("Main", HideValue, "_is_singbox")
o:depends("_hide", "1")
current_node_id = m:get(s.section, "node")
local node_value = s.fields["node"]:formvalue(s.section)
if node_value then
current_node_id = node_value
end
current_node = current_node_id and m:get(current_node_id) or {}
o = s:taboption("Main", HideValue, "node_save_before")
o.value = current_node[".name"]
o.cbid = function(self, section) return "node_save_before" end
-- Shunt Start
if (has_singbox or has_xray) and #nodes_table > 0 then
if #normal_list > 0 or #iface_list > 0 then
if current_node.protocol == "_shunt" then
local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall2/client/include/shunt_options.lua")
setfenv(shunt_lua, getfenv(1))(m, s, {
s_cfgid = s.section,
node_id = current_node_id,
node = current_node,
socks_list = socks_list,
urltest_list = urltest_list,
balancing_list = balancing_list,
iface_list = iface_list,
normal_list = normal_list,
verify_option = s.fields["node"],
tab = "Shunt",
tab_desc = translate("Shunt Rule")
tab_desc = translate("Shunt Rule"),
})
end
else
@@ -314,8 +311,8 @@ loglevel:value("error")
o = s:taboption("log", DummyValue, "_log", translate("Log File"))
o.rawhtml = true
o.cfgvalue = function(t, n)
local log_path = api.TMP_PATH .. "/acl/default.log"
local log_url = api.url("get_redir_log") .. "?id=default"
local log_path = api.TMP_PATH .. "/acl/acl_default.log"
local log_url = api.url("get_redir_log") .. "?id=acl_default"
return string.format(
'<code>%s</code>&nbsp;&nbsp;<input class="btn cbi-button cbi-button-apply" type="button" value="%s" onclick="window.open(\'%s\', \'_blank\')" />',
log_path,
@@ -334,6 +331,8 @@ s:tab("maintain", translate("Maintain"))
o = s:taboption("maintain", DummyValue, "")
o.template = m:template_path("/global/backup")
m:appendTemplate("/include/node_change", { verify_option = s.fields["node"], shunt_list = api.jsonc.stringify(shunt_list) })
-- [[ Socks Server ]]--
o = s:taboption("Main", Flag, "socks_enabled", "Socks " .. translate("Main switch"))
o.rmempty = false
@@ -417,7 +416,7 @@ for k, v in pairs(nodes_table) do
end
end
m:appendTemplate("/global/footer", {shunt_list = api.jsonc.stringify(shunt_list)})
m:appendTemplate("/global/footer")
m:appendTemplate("/cbi/sortable", {sectiontype = s2.sectiontype})

Some files were not shown because too many files have changed in this diff Show More