Compare commits

...
41 Commits
Author SHA1 Message Date
action bb875f0160 update 2026-09-17 17:06:35 2026-09-17 17:06:35 +08:00
action c191464ab9 update 2026-09-17 11:00:22 2026-09-17 11:00:22 +08:00
action e08f1e1181 update 2026-09-17 03:24:26 2026-09-17 03:24:26 +08:00
action cb9c2be524 update 2026-09-16 23:57:51 2026-09-16 23:57:51 +08:00
action 7f7cbbe9c8 update 2026-09-16 17:00:28 2026-09-16 17:00:28 +08:00
action f5923596c5 update 2026-09-16 10:57:20 2026-09-16 10:57:20 +08:00
action 1dab9d1364 update 2026-09-16 06:39:05 2026-09-16 06:39:05 +08:00
action d98dc86e1e update 2026-09-16 00:04:48 2026-09-16 00:04:48 +08:00
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
178 changed files with 6921 additions and 4431 deletions
+2 -2
View File
@@ -5,12 +5,12 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=brook PKG_NAME:=brook
PKG_VERSION:=20260101.0 PKG_VERSION:=20270101
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/txthinking/brook/tar.gz/v$(PKG_VERSION)? 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_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
PKG_LICENSE:=GPL-3.0 PKG_LICENSE:=GPL-3.0
+4 -4
View File
@@ -1,8 +1,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=clashoo PKG_NAME:=clashoo
PKG_SHORT_SHA:=8f54283 PKG_SHORT_SHA:=fbb6742
PKG_COMMIT_DATE:=2026.09.07 PKG_COMMIT_DATE:=2026.09.16
PKG_VERSION:=$(PKG_COMMIT_DATE)~$(PKG_SHORT_SHA) PKG_VERSION:=$(PKG_COMMIT_DATE)~$(PKG_SHORT_SHA)
PKG_RELEASE:=1 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_SOURCE_SUBDIR:=$(PKG_NAME)-alpha-$(PKG_SHORT_SHA)
PKG_BUILD_DIR:=$(BUILD_DIR)/$(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_URL:=https://github.com/kenzok8/openwrt-clashoo/releases/download/mihomo-src
PKG_SOURCE_VERSION:=8f54283e97728859d88f45025bab250504337687 PKG_SOURCE_VERSION:=fbb674227d5cf5a3796a1dd1451849fa1872884a
PKG_HASH:=28db96ba63d8f4e79df122c1efe02fed485e1997cf1888e5d045c17908aa34a9 PKG_HASH:=ecdd7fb1d5847884b350317e09c6d366fe6ea57e96f3ac0cd928df110f541ac8
PKG_BUILD_VERSION:=alpha-$(PKG_SHORT_SHA) PKG_BUILD_VERSION:=alpha-$(PKG_SHORT_SHA)
PKG_LICENSE:=GPL3.0+ PKG_LICENSE:=GPL3.0+
+3
View File
@@ -47,6 +47,9 @@ config clashoo 'config'
option fallback_filter_geoip '0' option fallback_filter_geoip '0'
list fallback_filter_ipcidr '240.0.0.0/4' list fallback_filter_ipcidr '240.0.0.0/4'
option dns_respect_rules '1' 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 dns_migrated '1'
option block_quic '0' option block_quic '0'
option singbox_independent_cache '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)" "$(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() { revert_dns() {
#=========================================================================================================================== #===========================================================================================================================
local _before _after local _before _after
@@ -803,7 +808,7 @@ revert_dns() {
return 0 return 0
fi fi
uci commit dhcp 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 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() { backfill_custom_sniffer_overrides() {
[ "$CLASHOO_KEEP_SNIFFER" = "1" ] || return 0 [ "$CLASHOO_KEEP_SNIFFER" = "1" ] || return 0
[ "$(uci -q get clashoo.config.sniffer_streaming 2>/dev/null)" = "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() { yml_change() {
CLASHOO_DNS_FORCE="$(_clashoo_dns_force_fields)" CLASHOO_DNS_FORCE="$(_clashoo_dns_force_fields)"
CLASHOO_DNS_PRESENT="$(_user_dns_fields "$CLASHOO_DNS_FORCE")" 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_FORCE
export CLASHOO_DNS_PRESENT export CLASHOO_DNS_PRESENT
export CLASHOO_DNS_INHERIT_MODE
export CLASHOO_DNS_INHERIT_NAMESERVER
CLASHOO_KEEP_FAKEIP_FILTER="" CLASHOO_KEEP_FAKEIP_FILTER=""
CLASHOO_KEEP_SNIFFER="" CLASHOO_KEEP_SNIFFER=""
if command -v yq >/dev/null 2>&1 && [ -s "$CONFIG_YAML" ]; then if command -v yq >/dev/null 2>&1 && [ -s "$CONFIG_YAML" ]; then
@@ -1583,8 +1688,7 @@ singbox_dns_change() {
fi fi
if [ "$(_dnsmasq_state_digest)" != "$_sb_dns_before" ]; then if [ "$(_dnsmasq_state_digest)" != "$_sb_dns_before" ]; then
uci commit dhcp >/dev/null 2>&1 uci commit dhcp >/dev/null 2>&1
/etc/init.d/dnsmasq stop >/dev/null 2>&1 _reload_dnsmasq >/dev/null 2>&1
/etc/init.d/dnsmasq start >/dev/null 2>&1
else else
uci -q revert dhcp >/dev/null 2>&1 uci -q revert dhcp >/dev/null 2>&1
fi 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 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 yq e -i 'explode(.)' "$CONFIG_YAML" 2>/dev/null || true
fi 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 # 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 /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 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 if [ "$(_dnsmasq_state_digest)" != "$_dnsmasq_digest_before" ]; then
log_msg "Reloading DNS resolver" "重启 DNS 解析" log_msg "Reloading DNS resolver" "重启 DNS 解析"
/etc/init.d/dnsmasq stop >/dev/null 2>&1 _reload_dnsmasq >/dev/null 2>&1
/etc/init.d/dnsmasq start >/dev/null 2>&1
fi fi
restore >/dev/null 2>&1 restore >/dev/null 2>&1
@@ -2041,7 +2145,7 @@ boot(){
runtime_state_set "health_status" "starting" runtime_state_set "health_status" "starting"
runtime_state_set "health_detail" "boot_starting" runtime_state_set "health_detail" "boot_starting"
runtime_state_set "updated_at" "$(date +%s)" 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 # (~20-30s on LXC). Doing this synchronously in boot() blocks rcS S99 stage
# and during that window sshd/uhttpd refuse connections (kex reset). Detach # and during that window sshd/uhttpd refuse connections (kex reset). Detach
# to background with a small settle delay so the rcS pipeline finishes and # 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"'"}' \ -X PUT -d '{"path":"'"$CONFIG_YAML"'"}' \
"http://${_lip}:${_dp}/configs?force=true" >/dev/null 2>&1 "http://${_lip}:${_dp}/configs?force=true" >/dev/null 2>&1
backend_apply_rules 2>/dev/null 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 runtime_run_health_check_settle >/dev/null 2>&1
fi fi
} }
@@ -939,6 +939,7 @@ set clashoo_china6 {
2402:7240::/32, 2402:7240::/32,
2402:72a0::/32, 2402:72a0::/32,
2402:72c0::/32, 2402:72c0::/32,
2402:73e0::/32,
2402:7540::/32, 2402:7540::/32,
2402:75c0::/32, 2402:75c0::/32,
2402:7740::/32, 2402:7740::/32,
@@ -1246,11 +1247,12 @@ set clashoo_china6 {
2403:6280::/32, 2403:6280::/32,
2403:62c0::/32, 2403:62c0::/32,
2403:6380::/42, 2403:6380::/42,
2403:6380:41::/48,
2403:6380:43::/48, 2403:6380:43::/48,
2403:6380:44::/46, 2403:6380:44::/46,
2403:6380:48::/45, 2403:6380:48::/45,
2403:6380:50::/44, 2403:6380:50::/44,
2403:6380:70::/44, 2403:6380:60::/43,
2403:6380:80::/41, 2403:6380:80::/41,
2403:6380:100::/40, 2403:6380:100::/40,
2403:6380:200::/39, 2403:6380:200::/39,
@@ -2036,7 +2038,7 @@ set clashoo_china6 {
2406:840:a18::/45, 2406:840:a18::/45,
2406:840:a20::/44, 2406:840:a20::/44,
2406:840:a30::/48, 2406:840:a30::/48,
2406:840:a32::/47, 2406:840:a33::/48,
2406:840:a34::/46, 2406:840:a34::/46,
2406:840:a38::/45, 2406:840:a38::/45,
2406:840:a40::/42, 2406:840:a40::/42,
@@ -2337,7 +2339,10 @@ set clashoo_china6 {
2406:840:e57f::/48, 2406:840:e57f::/48,
2406:840:e580::/41, 2406:840:e580::/41,
2406:840:e610::/44, 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:e640::/43,
2406:840:e660::/46, 2406:840:e660::/46,
2406:840:e664::/47, 2406:840:e664::/47,
@@ -2448,6 +2453,10 @@ set clashoo_china6 {
2406:840:f600::/42, 2406:840:f600::/42,
2406:840:f640::/43, 2406:840:f640::/43,
2406:840:f670::/44, 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:f690::/44,
2406:840:f6a0::/43, 2406:840:f6a0::/43,
2406:840:f6c0::/42, 2406:840:f6c0::/42,
@@ -3039,7 +3048,6 @@ set clashoo_china6 {
2408:4008::/29, 2408:4008::/29,
2408:4010::/30, 2408:4010::/30,
2408:4014::/31, 2408:4014::/31,
2408:4016:1::/48,
2408:4016:2::/47, 2408:4016:2::/47,
2408:4016:4::/46, 2408:4016:4::/46,
2408:4016:8::/45, 2408:4016:8::/45,
@@ -920,7 +920,9 @@ set clashoo_china {
59.153.136.0/22, 59.153.136.0/22,
59.153.152.0/22, 59.153.152.0/22,
59.153.164.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.176.0/20,
59.153.192.0/22, 59.153.192.0/22,
59.155.0.0/16, 59.155.0.0/16,
@@ -951,6 +953,7 @@ set clashoo_china {
61.29.128.0/18, 61.29.128.0/18,
61.29.192.0/19, 61.29.192.0/19,
61.29.224.0/20, 61.29.224.0/20,
61.29.251.0/24,
61.45.128.0/18, 61.45.128.0/18,
61.45.224.0/20, 61.45.224.0/20,
61.47.128.0/18, 61.47.128.0/18,
@@ -2186,6 +2189,7 @@ set clashoo_china {
103.143.132.0/22, 103.143.132.0/22,
103.143.174.0/23, 103.143.174.0/23,
103.143.228.0/23, 103.143.228.0/23,
103.144.41.0/24,
103.144.66.0/23, 103.144.66.0/23,
103.144.70.0/23, 103.144.70.0/23,
103.144.72.0/23, 103.144.72.0/23,
@@ -2341,7 +2345,7 @@ set clashoo_china {
103.176.244.0/23, 103.176.244.0/23,
103.177.28.0/23, 103.177.28.0/23,
103.177.44.0/23, 103.177.44.0/23,
103.177.70.0/23, 103.177.71.0/24,
103.177.162.0/23, 103.177.162.0/23,
103.178.240.0/23, 103.178.240.0/23,
103.179.76.0/22, 103.179.76.0/22,
@@ -2754,6 +2758,7 @@ set clashoo_china {
103.238.24.0/21, 103.238.24.0/21,
103.238.32.0/21, 103.238.32.0/21,
103.238.40.0/22, 103.238.40.0/22,
103.238.46.0/23,
103.238.48.0/21, 103.238.48.0/21,
103.238.56.0/22, 103.238.56.0/22,
103.238.88.0/21, 103.238.88.0/21,
@@ -3224,7 +3229,10 @@ set clashoo_china {
114.112.228.0/24, 114.112.228.0/24,
114.112.230.0/23, 114.112.230.0/23,
114.112.234.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.0.0/17,
114.113.128.0/21, 114.113.128.0/21,
114.113.140.0/22, 114.113.140.0/22,
@@ -3394,7 +3402,9 @@ set clashoo_china {
117.134.128.0/18, 117.134.128.0/18,
117.134.205.0/24, 117.134.205.0/24,
117.134.207.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.232.0/21,
117.134.240.0/20, 117.134.240.0/20,
117.135.0.0/16, 117.135.0.0/16,
@@ -3746,7 +3756,6 @@ set clashoo_china {
123.49.236.0/24, 123.49.236.0/24,
123.49.240.0/24, 123.49.240.0/24,
123.49.242.0/23, 123.49.242.0/23,
123.49.245.0/24,
123.49.248.0/21, 123.49.248.0/21,
123.50.160.0/19, 123.50.160.0/19,
123.52.0.0/14, 123.52.0.0/14,
@@ -3981,7 +3990,14 @@ set clashoo_china {
140.179.0.0/16, 140.179.0.0/16,
140.205.0.0/16, 140.205.0.0/16,
140.206.0.0/15, 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.224.0.0/16,
140.237.0.0/16, 140.237.0.0/16,
140.240.0.0/16, 140.240.0.0/16,
@@ -4214,6 +4230,7 @@ set clashoo_china {
163.47.4.0/22, 163.47.4.0/22,
163.52.28.0/23, 163.52.28.0/23,
163.52.76.0/23, 163.52.76.0/23,
163.52.108.0/23,
163.53.0.0/20, 163.53.0.0/20,
163.53.36.0/22, 163.53.36.0/22,
163.53.40.0/21, 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 [ -f "$CONFIG_YAML" ] || exit 0
. /usr/share/clashoo/runtime/primary_group.sh
# __PROXY__ placeholder -> primary proxy group (cached by RPC); # __PROXY__ placeholder -> primary proxy group (cached by RPC);
# fallback to GLOBAL (built-in mihomo group) # 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" [ -n "$PRIMARY_GROUP" ] || PRIMARY_GROUP="GLOBAL"
append=$(uci get clashoo.config.append_rules 2>/dev/null) 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 = {}; let dns_force = {};
for (let f in split(s(getenv('CLASHOO_DNS_FORCE'), ''), ',')) for (let f in split(s(getenv('CLASHOO_DNS_FORCE'), ''), ','))
if (length(trim(f))) dns_force[trim(f)] = true; 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', let dns_role_fields = ['nameserver', 'proxy-server-nameserver', 'direct-nameserver',
'default-nameserver', 'nameserver-policy', 'respect-rules']; 'default-nameserver', 'nameserver-policy', 'respect-rules'];
let had_user_dns_roles = false; let had_user_dns_roles = false;
@@ -158,8 +166,15 @@ uci.foreach('clashoo', 'dnsservers', function(sec) {
if (!dns_roles[role]) dns_roles[role] = []; if (!dns_roles[role]) dns_roles[role] = [];
push(dns_roles[role], srv); 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_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'); let bootstrap = a('default_nameserver');
if (bootstrap == null) bootstrap = a('defaul_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; cfg['dns']['nameserver-policy'] = policies;
let respect_rules = a('dns_respect_rules') == null ? true : ab('dns_respect_rules'); let respect_rules = a('dns_respect_rules') == null ? true : ab('dns_respect_rules');
if (respect_rules && length(cfg['dns']['proxy-server-nameserver'] || []) if (dns_force['respect-rules'])
&& (!dns_present['respect-rules'] || dns_force['respect-rules']) && !dns_present['prefer-h3']) 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; cfg['dns']['respect-rules'] = true;
/* profile */ /* 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 case "$file" in
clashoo_*_"$ARCH".ipk) clashoo_*_"$ARCH".ipk)
v="${file#clashoo_}" 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-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' ;; luci-i18n-clashoo-zh-cn_*.ipk) printf '%s\n' "$file" | sed -n 's/^luci-i18n-clashoo-zh-cn_\(.*\)_all\.ipk$/\1/p' ;;
clashoo-*.apk) clashoo-*.apk)
+3 -3
View File
@@ -5,13 +5,13 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=dae PKG_NAME:=dae
PKG_VERSION:=2026.09.06 PKG_VERSION:=2026.09.12
PKG_RELEASE:=1 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_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/dae-src
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_HASH:=bb80d29edd0faecaa4184c32ba43d601271d60cfc7fe920c3fa5ad4be91e2dbc PKG_HASH:=187058462a1fd9eeb9885f4971ccf4bc81358533e71730d8509bd7968624c1c7
PKG_LICENSE:=AGPL-3.0-only PKG_LICENSE:=AGPL-3.0-only
PKG_LICENSE_FILE:=LICENSE PKG_LICENSE_FILE:=LICENSE
+3 -3
View File
@@ -5,13 +5,13 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=daed PKG_NAME:=daed
PKG_VERSION:=2026.09.06 PKG_VERSION:=2026.09.12
PKG_RELEASE:=1 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_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_HASH:=00979296e45ef7f4aab704ca131e9f686194b61b2500b7483c48722366278d36 PKG_HASH:=a0181f7298552497ed193e683a54b1df77f2d5441524d61989f3e3e3cf6eac51
PKG_LICENSE:=AGPL-3.0-only MIT PKG_LICENSE:=AGPL-3.0-only MIT
PKG_LICENSE_FILES:=LICENSE wing/LICENSE PKG_LICENSE_FILES:=LICENSE wing/LICENSE
+83 -4
View File
@@ -1,8 +1,65 @@
#!/bin/sh #!/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() { 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 for pid in $(ip netns pids daens 2>/dev/null); do
kill "$pid" 2>/dev/null kill "$pid" 2>/dev/null
done done
@@ -14,14 +71,36 @@ daed_cleanup_runtime() {
done done
fi 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 if ! ip netns del daens 2>/dev/null; then
umount -l /run/netns/daens 2>/dev/null 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 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 [ ! -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 ! ip link show dae0 >/dev/null 2>&1 || return 1
return $rc
} }
+117 -15
View File
@@ -1,29 +1,131 @@
#!/bin/sh #!/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 . /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 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 exit 1
fi fi
# Keep daed as a child so a panic or unexpected exit is followed by an # Keep daed as a child so post-exit cleanup runs before procd can
# immediate teardown of all kernel/runtime state before procd can respawn us. # respawn it.
child_pid= child_pid=
cleanup_child() { pending_signal=
[ -n "$child_pid" ] && kill "$child_pid" 2>/dev/null shutdown_signal=
} shutdown_elapsed=0
trap cleanup_child TERM INT 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
}
exit_with_signal() {
local sig="$1"
trap - TERM INT HUP QUIT
kill -"$sig" "$$" 2>/dev/null
exit 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 "$@" & /usr/bin/daed "$@" &
child_pid=$! child_pid=$!
wait "$child_pid" if [ -n "$pending_signal" ]; then
status=$? sig="$pending_signal"
child_pid= pending_signal=
trap - TERM INT forward_signal "$sig"
if ! daed_cleanup_runtime; then
echo "daed: runtime cleanup after exit failed" >&2
status=1
fi 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
# 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" exit "$status"
fi
exit "$cleanup_status"
+37 -13
View File
@@ -1,5 +1,7 @@
#!/bin/sh /etc/rc.common #!/bin/sh /etc/rc.common
# Copyright (C) 2023 Tianling Shen <cnsztl@immortalwrt.org> # 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 USE_PROCD=1
START=99 START=99
@@ -10,18 +12,28 @@ LOG="/var/log/daed/daed.log"
. /usr/share/daed/cleanup.sh . /usr/share/daed/cleanup.sh
log() {
logger -t daed-init "$@"
}
start_service() { start_service() {
log "start: begin"
config_load "$CONF" config_load "$CONF"
local enabled local enabled
config_get_bool enabled "config" "enabled" "0" 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 local listen_addr log_maxbackups log_maxsize
config_get listen_addr "config" "listen_addr" "0.0.0.0:2023" config_get listen_addr "config" "listen_addr" "0.0.0.0:2023"
config_get log_maxbackups "config" "log_maxbackups" "1" config_get log_maxbackups "config" "log_maxbackups" "1"
config_get log_maxsize "config" "log_maxsize" "5" 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_open_instance "$CONF"
procd_set_param env DAE_LOCATION_ASSET="/usr/share/v2ray" TZ="$(uci -q get system.@system[0].zonename)" procd_set_param env DAE_LOCATION_ASSET="/usr/share/v2ray" TZ="$(uci -q get system.@system[0].zonename)"
procd_set_param command "$PROG" run procd_set_param command "$PROG" run
@@ -33,25 +45,37 @@ start_service() {
procd_set_param limits core="unlimited" procd_set_param limits core="unlimited"
procd_set_param limits nofile="1000000 1000000" procd_set_param limits nofile="1000000 1000000"
# Avoid an endless crash/respawn loop which can repeatedly reattach dae's # daed-guard escalates its child after 20 seconds. Leave time for
# data-plane hooks and make the router management plane unreachable. # reap and post-exit cleanup before procd kills the wrapper.
procd_set_param respawn 3600 5 5 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 stdout 1
procd_set_param stderr 1 procd_set_param stderr 1
procd_close_instance procd_close_instance
log "start: procd_open_instance done"
} }
stop_service() { stop_service() {
rm -f "$LOG" log "stop: begin"
daed_cleanup_runtime # 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=""
restart() { if [ -d /sys/fs/bpf/daed ]; then
stop pinned=$(ls /sys/fs/bpf/daed 2>/dev/null | tr '\n' ' ')
sleep 1 fi
daed_cleanup_runtime if ip netns list 2>/dev/null | grep -q '^daens'; then
start ns_left="daens"
fi
log "stop: pre-stop state — bpf_pin_entries=[${pinned:-none}] netns_left=[${ns_left:-none}]"
} }
service_triggers() { service_triggers() {
+11 -9
View File
@@ -1,8 +1,6 @@
# SPDX-License-Identifier: GPL-3.0-only # SPDX-License-Identifier: GPL-3.0-only
# #
# Copyright (C) 2021-2023 sirpdboy <herboy2008@gmail.com> # Copyright (C) 2021-2026 sirpdboy <herboy2008@gmail.com>
#
# This is free software, licensed under the Apache License, Version 2.0 .
# #
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
@@ -10,12 +8,12 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=ddns-go PKG_NAME:=ddns-go
PKG_VERSION:=6.17.7 PKG_VERSION:=6.17.7
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_VERSION:=6.17.7
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/jeessy2/ddns-go/tar.gz/v$(PKG_VERSION)? PKG_SOURCE_URL:=https://codeload.github.com/jeessy2/ddns-go/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=f7001004e092d9641aad5a94158e0b4cae4a53a7f5c7d96d5c6af3d246c56fcc PKG_HASH:=f7001004e092d9641aad5a94158e0b4cae4a53a7f5c7d96d5c6af3d246c56fcc
PKG_LICENSE:=MIT PKG_HASH:=f7001004e092d9641aad5a94158e0b4cae4a53a7f5c7d96d5c6af3d246c56fcc
PKG_LICENSE_FILES:=LICENSE PKG_LICENSE_FILES:=LICENSE
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org> 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. support Alidns Dnspod Cloudflare Hicloud Callback Baiducloud porkbun GoDaddy Google Domains.
endef endef
define Package/ddns-go/conffiles
/etc/config/ddns-go
/etc/ddns-go/ddns-go-config.yaml
endef
define Package/ddns-go/install define Package/ddns-go/install
$(call GoPackage/Package/Install/Bin,$(1)) $(call GoPackage/Package/Install/Bin,$(1))
$(INSTALL_DIR) $(1)/etc/init.d $(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) $(CURDIR)/file/ddns-go.init $(1)/etc/init.d/ddns-go $(INSTALL_DIR) $(1)/etc/config
$(INSTALL_BIN) $(CURDIR)/files/ddns-go.init $(1)/etc/init.d/ddns-go
$(INSTALL_DIR) $(1)/etc/uci-defaults $(INSTALL_CONF) $(CURDIR)/files/ddns-go.conf $(1)/etc/config/ddns-go
$(INSTALL_BIN) $(CURDIR)/file/luci-ddns-go.uci-default $(1)/etc/uci-defaults/luci-ddns-go
endef endef
$(eval $(call GoBinPackage,ddns-go)) $(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

+2 -2
View File
@@ -1,12 +1,12 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=docker PKG_NAME:=docker
PKG_VERSION:=29.8.0 PKG_VERSION:=29.8.1
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/docker/cli/tar.gz/v$(PKG_VERSION)? PKG_SOURCE_URL:=https://codeload.github.com/docker/cli/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=c5fadbc00c02dbecb1b7c9936e188baf9c80421a9107e7e9ad36a0923a0fc764 PKG_HASH:=55bcae5053f0914118d229658e2ac3a877dbdead6cb2c322e525d0c1e8bf78d2
PKG_BUILD_DIR:=$(BUILD_DIR)/cli-$(PKG_VERSION) PKG_BUILD_DIR:=$(BUILD_DIR)/cli-$(PKG_VERSION)
PKG_GIT_SHORT_COMMIT:=$(shell $(CURDIR)/git-short-commit.sh 'github.com/docker/cli' 'v$(PKG_VERSION)' '$(TMP_DIR)/git-short-commit/$(PKG_NAME)-$(PKG_VERSION)') PKG_GIT_SHORT_COMMIT:=$(shell $(CURDIR)/git-short-commit.sh 'github.com/docker/cli' 'v$(PKG_VERSION)' '$(TMP_DIR)/git-short-commit/$(PKG_NAME)-$(PKG_VERSION)')
+2 -2
View File
@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=dockerd PKG_NAME:=dockerd
PKG_VERSION:=29.8.0 PKG_VERSION:=29.8.1
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_LICENSE:=Apache-2.0 PKG_LICENSE:=Apache-2.0
PKG_LICENSE_FILES:=LICENSE PKG_LICENSE_FILES:=LICENSE
@@ -10,7 +10,7 @@ PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_GIT_URL:=github.com/moby/moby PKG_GIT_URL:=github.com/moby/moby
PKG_GIT_REF:=docker-v$(PKG_VERSION) PKG_GIT_REF:=docker-v$(PKG_VERSION)
PKG_SOURCE_URL:=https://codeload.$(PKG_GIT_URL)/tar.gz/$(PKG_GIT_REF)? PKG_SOURCE_URL:=https://codeload.$(PKG_GIT_URL)/tar.gz/$(PKG_GIT_REF)?
PKG_HASH:=e75ffb5d2ddc1fd98138fdb5e29f707b59f415ec8697e73b8bbdf8bbbb4be8eb PKG_HASH:=94be9d6940b613676335fc494e617b4acf98676435b3744f32649ed72114bd58
PKG_GIT_SHORT_COMMIT:=$(shell $(CURDIR)/git-short-commit.sh '$(PKG_GIT_URL)' '$(PKG_GIT_REF)' '$(TMP_DIR)/git-short-commit/$(PKG_NAME)-$(PKG_VERSION)') PKG_GIT_SHORT_COMMIT:=$(shell $(CURDIR)/git-short-commit.sh '$(PKG_GIT_URL)' '$(PKG_GIT_REF)' '$(TMP_DIR)/git-short-commit/$(PKG_NAME)-$(PKG_VERSION)')
PKG_MAINTAINER:=Gerard Ryan <G.M0N3Y.2503@gmail.com> PKG_MAINTAINER:=Gerard Ryan <G.M0N3Y.2503@gmail.com>
+3 -3
View File
@@ -9,9 +9,9 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://gn.googlesource.com/gn.git PKG_SOURCE_URL:=https://gn.googlesource.com/gn.git
PKG_SOURCE_DATE:=2026-09-03 PKG_SOURCE_DATE:=2026-09-11
PKG_SOURCE_VERSION:=4f6a76b64b8279e98004f541f8e136307efe5e01 PKG_SOURCE_VERSION:=cfcd774b98f3433e18b722f9a7ff06119825b8eb
PKG_MIRROR_HASH:=933dc8c8d745923437c9e730ab7c2a23a53562144513a4f634ad10381c23be44 PKG_MIRROR_HASH:=d8bea9ac89f9e87f36874601588f0d8d32221bf1ad8488f2fd9775abcddf4860
PKG_LICENSE:=BSD 3-Clause PKG_LICENSE:=BSD 3-Clause
PKG_LICENSE_FILES:=LICENSE PKG_LICENSE_FILES:=LICENSE
+2 -2
View File
@@ -3,7 +3,7 @@
#ifndef OUT_LAST_COMMIT_POSITION_H_ #ifndef OUT_LAST_COMMIT_POSITION_H_
#define OUT_LAST_COMMIT_POSITION_H_ #define OUT_LAST_COMMIT_POSITION_H_
#define LAST_COMMIT_POSITION_NUM 2553 #define LAST_COMMIT_POSITION_NUM 2563
#define LAST_COMMIT_POSITION "2553 (4f6a76b64b82)" #define LAST_COMMIT_POSITION "2563 (cfcd774b98f3)"
#endif // OUT_LAST_COMMIT_POSITION_H_ #endif // OUT_LAST_COMMIT_POSITION_H_
+2 -2
View File
@@ -5,12 +5,12 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=hysteria PKG_NAME:=hysteria
PKG_VERSION:=2.12.2 PKG_VERSION:=2.12.3
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/apernet/hysteria/tar.gz/app/v$(PKG_VERSION)? PKG_SOURCE_URL:=https://codeload.github.com/apernet/hysteria/tar.gz/app/v$(PKG_VERSION)?
PKG_HASH:=8db04a112e73685a1e5916d5d4d3df3ed897dbabc3e639fda4880b7ca9a7d18e PKG_HASH:=2679b76ab9cacbfd7574a48453325843865a347f1925858c4fb4fee3be132147
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-app-v$(PKG_VERSION) PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-app-v$(PKG_VERSION)
PKG_LICENSE:=MIT PKG_LICENSE:=MIT
+1 -1
View File
@@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-clashoo PKG_NAME:=luci-app-clashoo
PKG_VERSION:=1.30.0 PKG_VERSION:=1.30.0
PKG_RELEASE:=12 PKG_RELEASE:=15
PKG_MAINTAINER:=kenzok8 PKG_MAINTAINER:=kenzok8
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME) 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 callSmartFlushCache = rpc.declare({ object: 'luci.clashoo', method: 'smart_flush_cache', expect: {} });
const callListSingboxProfiles = rpc.declare({ object: 'luci.clashoo', method: 'list_singbox_profiles', 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 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 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 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: {} }); 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: '' }); }, listSingboxProfiles: function () { return L.resolveDefault(callListSingboxProfiles(), { profiles: [], active: '' }); },
getSingboxProfile: function (name) { return L.resolveDefault(callGetSingboxProfile(name), {}); }, 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), {}); }, setSingboxProfile: function (name) { return L.resolveDefault(callSetSingboxProfile(name), {}); },
deleteSingboxProfile: function (name) { return L.resolveDefault(callDeleteSingboxProfile(name), {}); }, deleteSingboxProfile: function (name) { return L.resolveDefault(callDeleteSingboxProfile(name), {}); },
createSingboxConfig: function (url, name) { return L.resolveDefault(callCreateSingboxConfig(url, 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 = s.option(form.Flag, 'dns_respect_rules', _("DNS Respect Rules"));
o.default = '1'; 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.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 = s.option(form.Value, 'dns_ecs', _("ECS Client Subnet"));
o.placeholder = _("Recommended blank"); o.placeholder = _("Recommended blank");
o.description = _("mihomo writes the ecs parameter to DNS URLs; sing-box writes dns.client_subnet. Leave empty to skip."); 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" msgid "OpenClash is enabled; disable it first to avoid rule conflicts"
msgstr "" msgstr ""
msgid "OpenClash/Nikki Loopback DNS Compatibility"
msgstr ""
msgid "Operation failed: " msgid "Operation failed: "
msgstr "" 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)" msgid "Other Configuration Files (upload / overwrite output)"
msgstr "" msgstr ""
@@ -1502,6 +1511,9 @@ msgstr ""
msgid "User-Agent (UA)" msgid "User-Agent (UA)"
msgstr "" 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" msgid "Username"
msgstr "" msgstr ""
+12
View File
@@ -909,6 +909,18 @@ msgstr "TCP 或 UDP 任意一个选择 TUN 时,该栈设置才会生效。"
msgid "OpenClash is enabled; disable it first to avoid rule conflicts" msgid "OpenClash is enabled; disable it first to avoid rule conflicts"
msgstr "OpenClash 已启用,请先停用以避免规则冲突" 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: " msgid "Operation failed: "
msgstr "操作失败: " msgstr "操作失败: "
@@ -1135,7 +1135,7 @@ return '_merged_' + s + '__' + t + '.yaml';
} }
function template_search_dirs() { 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) { function find_template_path(name) {
@@ -3318,6 +3318,8 @@ call: function(req) {
let tpl_src = trim(req.args?.template_source || ''); let tpl_src = trim(req.args?.template_source || '');
let sub_url = trim(req.args?.sub_url || ''); let sub_url = trim(req.args?.sub_url || '');
let output_name = ensure_yaml_name(req.args?.output_name || ''); 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'; let set_active = (req.args?.set_active || '') == '1';
if (!tpl_src) if (!tpl_src)
@@ -3361,9 +3363,9 @@ return { success: false, error: 'missing_yq', message: '缺少 yq,无法注入
if (!access(exploded, "r")) { if (!access(exploded, "r")) {
system("cp -f " + shell_quote(tmp_tpl) + " " + shell_quote(exploded)); system("cp -f " + shell_quote(tmp_tpl) + " " + shell_quote(exploded));
} }
/* 替换 proxy-providers.urlrule-providers 指向规则集保持原样 */ /* 替换 proxy-providers.url 并把 provider 名改成唯一名(同步 proxy-groups 的 use*/
let inject_cmd = "URL=" + shell_quote(sub_url) let inject_cmd = "URL=" + shell_quote(sub_url) + " NAME=" + shell_quote(uniq_name)
+ " yq e '.[\"proxy-providers\"][].url = env(URL)' " + " 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); + shell_quote(exploded) + " > " + shell_quote(tmp_out);
if (system(inject_cmd) != 0 || !access(tmp_out, "r")) { if (system(inject_cmd) != 0 || !access(tmp_out, "r")) {
system("rm -f " + shell_quote(exploded)); system("rm -f " + shell_quote(exploded));
@@ -3371,10 +3373,6 @@ return { success: false, error: 'missing_yq', message: '缺少 yq,无法注入
} }
system("rm -f " + shell_quote(exploded)); system("rm -f " + shell_quote(exploded));
if (!output_name) {
output_name = 'tpl-rewrite.yaml';
}
let out_path = out_dir + '/' + output_name; let out_path = out_dir + '/' + output_name;
if (system('mv -f ' + shell_quote(tmp_out) + ' ' + shell_quote(out_path)) != 0) if (system('mv -f ' + shell_quote(tmp_out) + ' ' + shell_quote(out_path)) != 0)
return { success: false, error: 'write_failed', message: '写入结果文件失败' }; return { success: false, error: 'write_failed', message: '写入结果文件失败' };
@@ -3382,16 +3380,16 @@ return { success: false, error: 'write_failed', message: '写入结果文件失
/* 删除 respect-rules(模板可能设了但没提供 proxy-server-nameserver */ /* 删除 respect-rules(模板可能设了但没提供 proxy-server-nameserver */
system("sed -i '/respect-rules/d' " + shell_quote(out_path) + " 2>/dev/null"); system("sed -i '/respect-rules/d' " + shell_quote(out_path) + " 2>/dev/null");
system('rm -f ' + shell_quote(tmp_tpl)); system('rm -f ' + shell_quote(tmp_tpl));
/* 替换 直连→DIRECT(兼容不同模板的命名差异) */ /* 只把独立引用项"直连"转成 DIRECT,组名里的"直连"不动 */
system("sed -i 's/直连/DIRECT/g' " + shell_quote(out_path) + " 2>/dev/null"); system("sed -i 's/^\\( *- *\\)直连$/\\1DIRECT/' " + shell_quote(out_path) + " 2>/dev/null");
system("yq e '(.proxy-groups[].proxies) |= sub(\"直连\", \"DIRECT\")' " + shell_quote(out_path) + " -i 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 等) */ /* 用 mixin.uc + yq merge 叠加 UCI 覆盖(端口、routing-mark、dns 等) */
let mixin_script = "/usr/share/clashoo/runtime/mixin.uc"; let mixin_script = "/usr/share/clashoo/runtime/mixin.uc";
if (access(mixin_script, "r") && system("command -v yq >/dev/null 2>&1") == 0) { if (access(mixin_script, "r") && system("command -v yq >/dev/null 2>&1") == 0) {
let mixin_yaml = "/tmp/_clashoo_mixin.yaml"; let mixin_yaml = "/tmp/_clashoo_mixin.yaml";
let merged_out = out_path + ".merged"; 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("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")) { if (access(merged_out, "r")) {
system("mv -f " + shell_quote(merged_out) + " " + shell_quote(out_path)); 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: { set_singbox_profile: {
args: { name: 'name' }, args: { name: 'name' },
call: function(req) { call: function(req) {
+1 -2
View File
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for fastnet LUCI_TITLE:=LuCI support for fastnet
LUCI_DEPENDS:=+fastnet LUCI_DEPENDS:=+fastnet
LUCI_PKGARCH:=all 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 MUST be empty for luci.mk
PKG_RELEASE:= PKG_RELEASE:=
LUCI_MINIFY_CSS:=0 LUCI_MINIFY_CSS:=0
@@ -17,4 +17,3 @@ LUCI_MINIFY_JS:=0
include $(TOPDIR)/feeds/luci/luci.mk include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature # call BuildPackage - OpenWrt buildroot signature
+2 -2
View File
@@ -7,9 +7,9 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
LUCI_TITLE:=KaiPlus LUCI_TITLE:=KaiPlus
PKG_VERSION:=1.0.1 PKG_VERSION:=1.0.9
PKG_RELEASE:=1 PKG_RELEASE:=1
LUCI_DEPENDS:=+kaiplus +luci-compat LUCI_DEPENDS:=+kaiplus +luci-compat +luci-lib-linkeaseauth
LUCI_MINIFY_CSS:=0 LUCI_MINIFY_CSS:=0
LUCI_MINIFY_JS:=0 LUCI_MINIFY_JS:=0
@@ -62,4 +62,10 @@ base_path.default = "/apps/kaiplus/"
base_path.rmempty = false base_path.rmempty = false
base_path.readonly = true 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 return m
+1 -1
View File
@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-mosdns PKG_NAME:=luci-app-mosdns
PKG_VERSION:=1.7.13 PKG_VERSION:=1.7.14
PKG_RELEASE:=1 PKG_RELEASE:=1
LUCI_TITLE:=LuCI Support for mosdns LUCI_TITLE:=LuCI Support for mosdns
@@ -160,8 +160,18 @@ const injectStyles = () => {
'.btn-allow:hover { background: #059669 !important; color: #fff !important; }', '.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 { 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; }', '.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; } }', '@keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; } }',
'@media (prefers-color-scheme: dark) {', '@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-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-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); }', ' .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-time', style: 'font-size: 0.82rem; opacity: 0.7; white-space: nowrap;' }, formatTimestamp(item.timestamp)),
E('td', { E('td', {
class: 'td col-client mosdns-mono', 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 title: clientInfo.title
}, clientInfo.display), }, clientInfo.display),
E('td', { class: 'td col-domain', style: 'max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;', title: item.domain || '-' }, [ E('td', { class: 'td col-domain', style: 'max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;', title: item.domain || '-' }, [
@@ -887,22 +897,74 @@ const renderLogsTable = logsData => {
])); ]));
} }
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')
});
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', {}, [ return E('div', {}, [
E('table', { class: 'table cbi-section-table mosdns-table', style: 'margin-top: 0.25rem;' }, [ E('table', { class: 'table cbi-section-table mosdns-table', style: 'margin-top: 0.25rem; margin-bottom: 0;' }, [
E('tr', { class: 'tr table-titles' }, [ E('tr', { class: 'tr table-titles' }, [
E('th', { class: 'th col-time', style: 'width: 85px;' }, _('Time')), 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-client', style: 'width: 125px;' }, _('Client IP')),
E('th', { class: 'th col-domain' }, _('Domain & Record')), E('th', { class: 'th col-domain' }, _('Domain & Record')),
E('th', { class: 'th col-status', style: 'width: 90px;' }, _('Status')), E('th', { class: 'th col-status', style: 'width: 90px;' }, _('Status')),
E('th', { class: 'th col-answers' }, _('Answers')), E('th', { class: 'th col-answers' }, _('Answers')),
E('th', { class: 'th col-latency', style: 'width: 80px; text-align: right;' }, _('Elapsed')) E('th', { class: 'th col-latency', style: 'width: 95px; text-align: right;' }, _('Elapsed'))
]), ]),
...rows ...rows
]), ]),
E('div', { style: 'display: flex; justify-content: space-between; align-items: center; margin-top: 0.75rem;' }, [ E('div', {
E('span', { style: 'font-size: 0.85rem; opacity: 0.7;' }, _('Page %d / %d (%d entries)').format(pageIdx + 1, totalPages, total)), class: 'mosdns-pagination-bar'
E('div', { style: 'display: flex; gap: 0.5rem;' }, [ }, [
paginationLabel,
E('div', { class: 'mosdns-pagination-btns' }, [
E('button', { E('button', {
class: 'btn cbi-button cbi-button-action', class: 'btn cbi-button cbi-button-action',
disabled: pageIdx === 0 ? 'disabled' : null, disabled: pageIdx === 0 ? 'disabled' : null,
+77 -73
View File
@@ -17,19 +17,19 @@ msgstr ""
msgid "API Options" msgid "API Options"
msgstr "" 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:" msgid "Add domain to the blocklist to block DNS resolution:"
msgstr "" 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:" msgid "Add domain to the whitelist to permit DNS resolution:"
msgstr "" 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" msgid "Add to Blocklist"
msgstr "" 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" msgid "Add to Whitelist"
msgstr "" msgstr ""
@@ -90,7 +90,7 @@ msgstr ""
msgid "Aliyun Public DNS (DNS over QUIC)" msgid "Aliyun Public DNS (DNS over QUIC)"
msgstr "" 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" msgid "All Queries"
msgstr "" msgstr ""
@@ -99,8 +99,8 @@ msgstr ""
msgid "Another update is already in progress." msgid "Another update is already in progress."
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:805 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:815
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:897 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:957
msgid "Answers" msgid "Answers"
msgstr "" msgstr ""
@@ -108,7 +108,7 @@ msgstr ""
msgid "Apple domains optimization" msgid "Apple domains optimization"
msgstr "" 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 "" msgid ""
"Are you sure you want to clear all real-time query logs and top rankings?" "Are you sure you want to clear all real-time query logs and top rankings?"
msgstr "" msgstr ""
@@ -127,11 +127,11 @@ msgid ""
"rules through scheduled tasks." "rules through scheduled tasks."
msgstr "" 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" msgid "Average Processing Time"
msgstr "" 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" msgid "Avg Processing"
msgstr "" msgstr ""
@@ -155,8 +155,8 @@ msgid ""
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:31 #: 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:643
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:635 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:645
msgid "Block Lists" msgid "Block Lists"
msgstr "" msgstr ""
@@ -164,11 +164,11 @@ msgstr ""
msgid "Block PTR" msgid "Block PTR"
msgstr "" 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" msgid "Blocked Only"
msgstr "" 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" msgid "Blocked by Filters"
msgstr "" msgstr ""
@@ -190,16 +190,16 @@ msgstr ""
msgid "Cache Prefetching" msgid "Cache Prefetching"
msgstr "" 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" msgid "Cached Only"
msgstr "" 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" msgid "Cached Queries"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:730 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:740
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1058 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1120
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
@@ -229,7 +229,7 @@ msgstr ""
msgid "Cisco Public DNS (208.67.222.222)" msgid "Cisco Public DNS (208.67.222.222)"
msgstr "" 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" msgid "Clear"
msgstr "" msgstr ""
@@ -237,22 +237,22 @@ msgstr ""
msgid "Clear logs" msgid "Clear logs"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1049 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1111
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1052 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1114
msgid "Clear query logs" msgid "Clear query logs"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:861 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:871
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:878 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:888
msgid "Click to view full details" msgid "Click to view full details"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:787 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:797
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:894 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:954
msgid "Client IP" msgid "Client IP"
msgstr "" 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 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:35
msgid "Close" msgid "Close"
msgstr "" msgstr ""
@@ -292,7 +292,7 @@ msgstr ""
msgid "Configuration Editor" msgid "Configuration Editor"
msgstr "" 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" msgid "Confirm"
msgstr "" msgstr ""
@@ -330,7 +330,7 @@ msgstr ""
msgid "DNS Forward" msgid "DNS Forward"
msgstr "" 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" msgid "DNS Queries Total"
msgstr "" msgstr ""
@@ -374,20 +374,20 @@ msgstr ""
msgid "DoH/TCP/DoT Connection Multiplexing idle timeout (default 30 seconds)" msgid "DoH/TCP/DoT Connection Multiplexing idle timeout (default 30 seconds)"
msgstr "" 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" msgid "Domain & Record"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:627 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:637
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:655 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:665
msgid "Domain name cannot be empty." msgid "Domain name cannot be empty."
msgstr "" 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:" msgid "Domain:"
msgstr "" 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" msgid "Elapsed"
msgstr "" msgstr ""
@@ -425,6 +425,10 @@ msgstr ""
msgid "Enabled" msgid "Enabled"
msgstr "" 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 #: 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" msgid "Enter the GeoIP.dat category to be exported, Allow add multiple tags"
msgstr "" msgstr ""
@@ -478,7 +482,7 @@ msgstr ""
msgid "Failed to clean logs." msgid "Failed to clean logs."
msgstr "" 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:" msgid "Failed to clear query logs:"
msgstr "" msgstr ""
@@ -486,15 +490,15 @@ msgstr ""
msgid "Failed to start update." msgid "Failed to start update."
msgstr "" 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:" msgid "Failed to update query logs:"
msgstr "" 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" msgid "Failed to update rule file: %s"
msgstr "" 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" msgid "First Page / Resume"
msgstr "" msgstr ""
@@ -595,10 +599,10 @@ msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:410 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:410
msgid "" msgid ""
"IPv4 CIDR: <a href=\"https://www.cloudflare.com/ips-v4\" " "IPv4 CIDR: <a href=\"https://www.cloudflare.com/ips-v4\" target=\"_blank"
"target=\"_blank\">https://www.cloudflare.com/ips-v4</a> <br /> IPv6 CIDR: <a " "\">https://www.cloudflare.com/ips-v4</a> <br /> IPv6 CIDR: <a href=\"https://"
"href=\"https://www.cloudflare.com/ips-v6\" target=\"_blank\">https://www." "www.cloudflare.com/ips-v6\" target=\"_blank\">https://www.cloudflare.com/ips-"
"cloudflare.com/ips-v6</a>" "v6</a>"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:190 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:190
@@ -621,7 +625,7 @@ msgstr ""
msgid "Info" msgid "Info"
msgstr "" 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" msgid "Intercept"
msgstr "" msgstr ""
@@ -631,7 +635,7 @@ msgid ""
"seconds)." "seconds)."
msgstr "" 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" msgid "Latency"
msgstr "" 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:33
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:106 #: 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/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 #: luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json:3
msgid "MosDNS" msgid "MosDNS"
msgstr "" 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 "" msgid ""
"MosDNS API is unreachable. Please ensure MosDNS is running and stats_api " "MosDNS API is unreachable. Please ensure MosDNS is running and stats_api "
"plugin is enabled." "plugin is enabled."
@@ -739,15 +743,15 @@ msgstr ""
msgid "Netflix, Disney+, Hulu and streaming media rules list will use this DNS" msgid "Netflix, Disney+, Hulu and streaming media rules list will use this DNS"
msgstr "" 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" msgid "Next"
msgstr "" 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." msgid "No DNS answer records returned."
msgstr "" 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" msgid "No data available"
msgstr "" msgstr ""
@@ -755,19 +759,19 @@ msgstr ""
msgid "No log data." msgid "No log data."
msgstr "" 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." msgid "No query log entries found."
msgstr "" 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)" msgid "Page %d / %d (%d entries)"
msgstr "" 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" msgid "Per-query speed"
msgstr "" 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" msgid "Permit"
msgstr "" msgstr ""
@@ -802,7 +806,7 @@ msgstr ""
msgid "Prevent DNS Leaks" msgid "Prevent DNS Leaks"
msgstr "" 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" msgid "Previous"
msgstr "" msgstr ""
@@ -821,7 +825,7 @@ msgstr ""
msgid "Quad9 Public DNS (9.9.9.9)" msgid "Quad9 Public DNS (9.9.9.9)"
msgstr "" 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" msgid "Query Log Details"
msgstr "" msgstr ""
@@ -831,7 +835,7 @@ msgid ""
"consume more memory)" "consume more memory)"
msgstr "" 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." msgid "Query logs cleared successfully."
msgstr "" msgstr ""
@@ -839,7 +843,7 @@ msgstr ""
msgid "RUNNING" msgid "RUNNING"
msgstr "" 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" msgid "Real-time Query Logs"
msgstr "" msgstr ""
@@ -869,7 +873,7 @@ msgstr ""
msgid "Ring Buffer Capacity" msgid "Ring Buffer Capacity"
msgstr "" 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" msgid "Rule Hit"
msgstr "" msgstr ""
@@ -889,7 +893,7 @@ msgstr ""
msgid "Save the cache locally and reload the cache dump on the next startup" msgid "Save the cache locally and reload the cache dump on the next startup"
msgstr "" 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..." msgid "Search domain or client IP..."
msgstr "" msgstr ""
@@ -901,7 +905,7 @@ msgstr ""
msgid "Starting update..." msgid "Starting update..."
msgstr "" 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 #: luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json:38
msgid "Statistics" msgid "Statistics"
msgstr "" msgstr ""
@@ -910,7 +914,7 @@ msgstr ""
msgid "Stats Dump" msgid "Stats Dump"
msgstr "" 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" msgid "Status"
msgstr "" msgstr ""
@@ -930,7 +934,7 @@ msgstr ""
msgid "TCP/DoT Connection Multiplexing" msgid "TCP/DoT Connection Multiplexing"
msgstr "" 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)" msgid "Target file: %s (supports domain matching rules)"
msgstr "" msgstr ""
@@ -966,20 +970,20 @@ msgid ""
"content in yaml format." "content in yaml format."
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:791 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:801
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:893 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:953
msgid "Time" msgid "Time"
msgstr "" 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" msgid "Top Blocked Domains"
msgstr "" 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" msgid "Top Clients"
msgstr "" 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" msgid "Top Queried Domains"
msgstr "" msgstr ""
@@ -1029,7 +1033,7 @@ msgstr ""
msgid "Updating Database..." msgid "Updating Database..."
msgstr "" 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" msgid "Upstream"
msgstr "" msgstr ""
@@ -1051,8 +1055,8 @@ msgid ""
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:23 #: 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:643
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:635 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:645
msgid "White Lists" msgid "White Lists"
msgstr "" msgstr ""
@@ -1070,15 +1074,15 @@ msgstr ""
msgid "https://gh-proxy.com" msgid "https://gh-proxy.com"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:427 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:437
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:540 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:550
msgid "● Live" msgid "● Live"
msgstr "" 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" msgid "● Live Auto-refresh"
msgstr "" 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)" msgid "❚❚ Paused (Page %d)"
msgstr "" msgstr ""
+81 -86
View File
@@ -27,19 +27,19 @@ msgstr "API 监听端口"
msgid "API Options" msgid "API Options"
msgstr "API 选项" 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:" msgid "Add domain to the blocklist to block DNS resolution:"
msgstr "将域名添加到黑名单以拦截 DNS 解析:" 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:" msgid "Add domain to the whitelist to permit DNS resolution:"
msgstr "将域名添加到白名单以放行 DNS 解析:" 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" msgid "Add to Blocklist"
msgstr "添加到黑名单" 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" msgid "Add to Whitelist"
msgstr "添加到白名单" msgstr "添加到白名单"
@@ -105,7 +105,7 @@ msgstr "阿里云公共 DNSDNS over HTTPS"
msgid "Aliyun Public DNS (DNS over QUIC)" msgid "Aliyun Public DNS (DNS over QUIC)"
msgstr "阿里云公共 DNSDNS 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" msgid "All Queries"
msgstr "所有查询" msgstr "所有查询"
@@ -114,8 +114,8 @@ msgstr "所有查询"
msgid "Another update is already in progress." msgid "Another update is already in progress."
msgstr "另一个更新正在进行中。" msgstr "另一个更新正在进行中。"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:805 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:815
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:897 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:957
msgid "Answers" msgid "Answers"
msgstr "应答结果" msgstr "应答结果"
@@ -123,7 +123,7 @@ msgstr "应答结果"
msgid "Apple domains optimization" msgid "Apple domains optimization"
msgstr "Apple 域名解析优化" 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 "" msgid ""
"Are you sure you want to clear all real-time query logs and top rankings?" "Are you sure you want to clear all real-time query logs and top rankings?"
msgstr "确定要清空所有实时查询日志和排行数据吗?" msgstr "确定要清空所有实时查询日志和排行数据吗?"
@@ -142,11 +142,11 @@ msgid ""
"rules through scheduled tasks." "rules through scheduled tasks."
msgstr "通过定时任务自动更新 GeoIP 和 GeoSite 数据库以及广告过滤规则。" 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" msgid "Average Processing Time"
msgstr "平均延迟" 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" msgid "Avg Processing"
msgstr "平均处理" 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/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:643
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:635 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:645
msgid "Block Lists" msgid "Block Lists"
msgstr "黑名单" msgstr "黑名单"
@@ -181,11 +181,11 @@ msgstr "黑名单"
msgid "Block PTR" msgid "Block PTR"
msgstr "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" msgid "Blocked Only"
msgstr "仅拦截" 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" msgid "Blocked by Filters"
msgstr "拦截总数" msgstr "拦截总数"
@@ -207,16 +207,16 @@ msgstr "自动保存缓存"
msgid "Cache Prefetching" msgid "Cache Prefetching"
msgstr "缓存预取" 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" msgid "Cached Only"
msgstr "仅缓存" 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" msgid "Cached Queries"
msgstr "缓存命中" msgstr "缓存命中"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:730 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:740
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1058 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1120
msgid "Cancel" msgid "Cancel"
msgstr "取消" msgstr "取消"
@@ -246,7 +246,7 @@ msgstr "思科公共 DNS208.67.220.220"
msgid "Cisco Public DNS (208.67.222.222)" msgid "Cisco Public DNS (208.67.222.222)"
msgstr "思科公共 DNS208.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" msgid "Clear"
msgstr "清空" msgstr "清空"
@@ -254,22 +254,22 @@ msgstr "清空"
msgid "Clear logs" msgid "Clear logs"
msgstr "清空日志" msgstr "清空日志"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1049 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1111
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1052 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:1114
msgid "Clear query logs" msgid "Clear query logs"
msgstr "清除查询日志" msgstr "清除查询日志"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:861 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:871
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:878 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:888
msgid "Click to view full details" msgid "Click to view full details"
msgstr "点击查看完整详情" msgstr "点击查看完整详情"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:787 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:797
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:894 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:954
msgid "Client IP" msgid "Client IP"
msgstr "客户端 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 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:35
msgid "Close" msgid "Close"
msgstr "关闭" msgstr "关闭"
@@ -309,7 +309,7 @@ msgstr "配置文件"
msgid "Configuration Editor" msgid "Configuration Editor"
msgstr "配置编辑器" 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" msgid "Confirm"
msgstr "确认" msgstr "确认"
@@ -348,7 +348,7 @@ msgstr "DNS 缓存大小"
msgid "DNS Forward" msgid "DNS Forward"
msgstr "DNS 转发" 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" msgid "DNS Queries Total"
msgstr "DNS 查询总数" msgstr "DNS 查询总数"
@@ -393,20 +393,20 @@ msgstr ""
msgid "DoH/TCP/DoT Connection Multiplexing idle timeout (default 30 seconds)" msgid "DoH/TCP/DoT Connection Multiplexing idle timeout (default 30 seconds)"
msgstr "DoH/TCP/DoT 连接复用空闲保持时间(默认 30 秒)" 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" msgid "Domain & Record"
msgstr "域名与类型" msgstr "域名与类型"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:627 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:637
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:655 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:665
msgid "Domain name cannot be empty." msgid "Domain name cannot be empty."
msgstr "域名不能为空" 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:" msgid "Domain:"
msgstr "域名:" 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" msgid "Elapsed"
msgstr "耗时" msgstr "耗时"
@@ -444,6 +444,10 @@ msgstr "启用此选项 fallback 策略会强制转发到远程 DNS"
msgid "Enabled" msgid "Enabled"
msgstr "启用" 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 #: 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" msgid "Enter the GeoIP.dat category to be exported, Allow add multiple tags"
msgstr "输入需要导出的 GeoIP.dat 类别条目,允许添加多个标签" msgstr "输入需要导出的 GeoIP.dat 类别条目,允许添加多个标签"
@@ -497,7 +501,7 @@ msgstr "导出目录:/var/mosdns"
msgid "Failed to clean logs." msgid "Failed to clean logs."
msgstr "清理日志失败:%s" 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:" msgid "Failed to clear query logs:"
msgstr "清除查询日志失败:" msgstr "清除查询日志失败:"
@@ -505,15 +509,15 @@ msgstr "清除查询日志失败:"
msgid "Failed to start update." msgid "Failed to start update."
msgstr "启动更新失败" 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:" msgid "Failed to update query logs:"
msgstr "更新查询日志失败:" 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" msgid "Failed to update rule file: %s"
msgstr "更新规则文件失败: %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" msgid "First Page / Resume"
msgstr "重置并恢复" msgstr "重置并恢复"
@@ -614,15 +618,15 @@ msgstr "IP 地址"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:410 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:410
msgid "" msgid ""
"IPv4 CIDR: <a href=\"https://www.cloudflare.com/ips-v4\" " "IPv4 CIDR: <a href=\"https://www.cloudflare.com/ips-v4\" target=\"_blank"
"target=\"_blank\">https://www.cloudflare.com/ips-v4</a> <br /> IPv6 CIDR: <a " "\">https://www.cloudflare.com/ips-v4</a> <br /> IPv6 CIDR: <a href=\"https://"
"href=\"https://www.cloudflare.com/ips-v6\" target=\"_blank\">https://www." "www.cloudflare.com/ips-v6\" target=\"_blank\">https://www.cloudflare.com/ips-"
"cloudflare.com/ips-v6</a>" "v6</a>"
msgstr "" msgstr ""
"IPv4 CIDR: <a href=\"https://www.cloudflare.com/ips-v4\" " "IPv4 CIDR: <a href=\"https://www.cloudflare.com/ips-v4\" target=\"_blank"
"target=\"_blank\">https://www.cloudflare.com/ips-v4</a> <br /> IPv6 CIDR: <a " "\">https://www.cloudflare.com/ips-v4</a> <br /> IPv6 CIDR: <a href=\"https://"
"href=\"https://www.cloudflare.com/ips-v6\" target=\"_blank\">https://www." "www.cloudflare.com/ips-v6\" target=\"_blank\">https://www.cloudflare.com/ips-"
"cloudflare.com/ips-v6</a>" "v6</a>"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:190 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:190
msgid "" msgid ""
@@ -644,7 +648,7 @@ msgstr "空闲超时"
msgid "Info" msgid "Info"
msgstr "信息" 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" msgid "Intercept"
msgstr "拦截" msgstr "拦截"
@@ -654,7 +658,7 @@ msgid ""
"seconds)." "seconds)."
msgstr "后台线程扫描缓存以进行预取的时间间隔(单位:秒)" 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" msgid "Latency"
msgstr "延迟" 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:33
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:106 #: 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/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 #: luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json:3
msgid "MosDNS" msgid "MosDNS"
msgstr "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 "" msgid ""
"MosDNS API is unreachable. Please ensure MosDNS is running and stats_api " "MosDNS API is unreachable. Please ensure MosDNS is running and stats_api "
"plugin is enabled." "plugin is enabled."
@@ -767,15 +771,15 @@ msgstr "未运行"
msgid "Netflix, Disney+, Hulu and streaming media rules list will use this DNS" msgid "Netflix, Disney+, Hulu and streaming media rules list will use this DNS"
msgstr "自定义 Netflix、Disney+、Hulu 以及 “流媒体” 规则列表的 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" msgid "Next"
msgstr "下一页" 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." msgid "No DNS answer records returned."
msgstr "无 DNS 应答记录" 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" msgid "No data available"
msgstr "暂无数据" msgstr "暂无数据"
@@ -783,19 +787,19 @@ msgstr "暂无数据"
msgid "No log data." msgid "No log data."
msgstr "无日志数据。" 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." msgid "No query log entries found."
msgstr "未找到查询日志记录。" 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)" msgid "Page %d / %d (%d entries)"
msgstr "第 %d / %d 页(%d 条记录)" 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" msgid "Per-query speed"
msgstr "单次查询速度" 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" msgid "Permit"
msgstr "放行" msgstr "放行"
@@ -832,7 +836,7 @@ msgstr "当剩余 TTL 小于此值(秒)时触发预取"
msgid "Prevent DNS Leaks" msgid "Prevent DNS Leaks"
msgstr "防止 DNS 泄漏" 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" msgid "Previous"
msgstr "上一页" msgstr "上一页"
@@ -851,7 +855,7 @@ msgstr "Quad9 公共 DNS149.112.112.112"
msgid "Quad9 Public DNS (9.9.9.9)" msgid "Quad9 Public DNS (9.9.9.9)"
msgstr "Quad9 公共 DNS9.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" msgid "Query Log Details"
msgstr "查询日志详情" msgstr "查询日志详情"
@@ -861,7 +865,7 @@ msgid ""
"consume more memory)" "consume more memory)"
msgstr "查询日志环形缓冲区容量(FIFO 覆盖,默认 2000,数值越大占用内存越多)" 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." msgid "Query logs cleared successfully."
msgstr "查询日志清除成功。" msgstr "查询日志清除成功。"
@@ -869,7 +873,7 @@ msgstr "查询日志清除成功。"
msgid "RUNNING" msgid "RUNNING"
msgstr "运行中" 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" msgid "Real-time Query Logs"
msgstr "实时查询日志" msgstr "实时查询日志"
@@ -901,7 +905,7 @@ msgstr "远程 DNS 服务器"
msgid "Ring Buffer Capacity" msgid "Ring Buffer Capacity"
msgstr "环形缓冲区容量" 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" msgid "Rule Hit"
msgstr "命中规则" msgstr "命中规则"
@@ -921,7 +925,7 @@ msgstr "保存统计数据与查询日志在本地,并在下次启动时重新
msgid "Save the cache locally and reload the cache dump on the next startup" msgid "Save the cache locally and reload the cache dump on the next startup"
msgstr "保存缓存到本地文件,以供下次启动时重新载入使用" 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..." msgid "Search domain or client IP..."
msgstr "搜索域名或客户端 IP..." msgstr "搜索域名或客户端 IP..."
@@ -933,7 +937,7 @@ msgstr "设置日志文件的最大容量(单位:MB)。"
msgid "Starting update..." msgid "Starting update..."
msgstr "正在启动更新..." 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 #: luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json:38
msgid "Statistics" msgid "Statistics"
msgstr "统计" msgstr "统计"
@@ -942,7 +946,7 @@ msgstr "统计"
msgid "Stats Dump" msgid "Stats Dump"
msgstr "自动保存统计" 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" msgid "Status"
msgstr "状态" msgstr "状态"
@@ -962,7 +966,7 @@ msgstr "支持本地文件,例如:file:///var/mosdns/example.txt"
msgid "TCP/DoT Connection Multiplexing" msgid "TCP/DoT Connection Multiplexing"
msgstr "TCP/DoT 连接复用" 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)" msgid "Target file: %s (supports domain matching rules)"
msgstr "目标文件: %s (支持域名匹配规则)" msgstr "目标文件: %s (支持域名匹配规则)"
@@ -1002,20 +1006,20 @@ msgstr ""
"这是文件 “/etc/mosdns/config_custom.yaml” 的内容,您的 MosDNS 配置将从此文件" "这是文件 “/etc/mosdns/config_custom.yaml” 的内容,您的 MosDNS 配置将从此文件"
"生成。仅接受 yaml 格式的配置内容。" "生成。仅接受 yaml 格式的配置内容。"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:791 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:801
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:893 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:953
msgid "Time" msgid "Time"
msgstr "时间" 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" msgid "Top Blocked Domains"
msgstr "拦截域名 (Top 10)" 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" msgid "Top Clients"
msgstr "活跃客户端 (Top 10)" 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" msgid "Top Queried Domains"
msgstr "查询域名 (Top 10)" msgstr "查询域名 (Top 10)"
@@ -1065,7 +1069,7 @@ msgstr "更新成功"
msgid "Updating Database..." msgid "Updating Database..."
msgstr "更新数据库..." 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" msgid "Upstream"
msgstr "上游服务器" 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/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:643
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:635 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:645
msgid "White Lists" msgid "White Lists"
msgstr "白名单" msgstr "白名单"
@@ -1110,24 +1114,15 @@ msgstr "信风公共 DNS114.114.115.115"
msgid "https://gh-proxy.com" msgid "https://gh-proxy.com"
msgstr "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:437
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:540 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:550
msgid "● Live" msgid "● Live"
msgstr "● 实时" 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" msgid "● Live Auto-refresh"
msgstr "● 实时刷新" 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)" msgid "❚❚ Paused (Page %d)"
msgstr "❚❚ 暂停 (第 %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_add_string "exec" "\$modify_ddns_ttl"
json_close_object json_close_object
json_add_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_add_string "exec" "\$modify_ttl"
json_close_object json_close_object
[ "$cloudflare" -eq 1 ] && { [ "$cloudflare" -eq 1 ] && {
@@ -571,6 +575,22 @@ generate_config() {
json_add_array "args" json_add_array "args"
json_add_object json_add_object
json_add_string "matches" "qname \$blocklist" 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_add_string "exec" "reject 3"
json_close_object json_close_object
json_add_object json_add_object
@@ -72,7 +72,7 @@ function get_adlist() {
let adblock = uci_cursor.get('mosdns', 'config', 'adblock'); let adblock = uci_cursor.get('mosdns', 'config', 'adblock');
if (adblock !== '1') { 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'); exec_sys('rm -rf /etc/mosdns/rule/adlist /etc/mosdns/rule/.ad_source');
writefile('/var/mosdns/disable-ads.txt', ''); writefile('/var/mosdns/disable-ads.txt', '');
print("/var/mosdns/disable-ads.txt\n"); print("/var/mosdns/disable-ads.txt\n");
+9 -1
View File
@@ -7,7 +7,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall PKG_NAME:=luci-app-passwall
PKG_VERSION:=26.9.1 PKG_VERSION:=26.9.16
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_PO_VERSION:=$(PKG_VERSION) PKG_PO_VERSION:=$(PKG_VERSION)
@@ -164,9 +164,17 @@ define Package/$(PKG_NAME)/conffiles
/usr/share/passwall/rules/domains_excluded /usr/share/passwall/rules/domains_excluded
endef 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 define Package/$(PKG_NAME)/postrm
#!/bin/sh #!/bin/sh
rm -f $${IPKG_INSTROOT}/usr/share/passwall/rules/*.nft rm -f $${IPKG_INSTROOT}/usr/share/passwall/rules/*.nft
rm -rf $${IPKG_INSTROOT}/tmp/etc/passwall_tmp
exit 0 exit 0
endef endef
@@ -61,14 +61,11 @@ function getOption(config, section, opt) {
const id = `cbid.${config}.${section}.${opt}`; const id = `cbid.${config}.${section}.${opt}`;
obj = document.getElementsByName(id)[0] || document.getElementById(id); obj = document.getElementsByName(id)[0] || document.getElementById(id);
if (obj) { if (obj) {
//lua luci combobox
const combobox = document.getElementById('cbi.combobox.' + id); const combobox = document.getElementById('cbi.combobox.' + id);
if (combobox) { if (combobox) {
obj.combobox = combobox; obj.combobox = combobox;
} }
const div = document.getElementById(id);
if (div && div.getElementsByTagName("li").length > 0) {
obj = div;
}
return obj; return obj;
} else { } else {
return null; return null;
@@ -479,7 +479,21 @@ function add_node()
uci_set(uid, "group", group) uci_set(uid, "group", group)
end end
if api.is_finded("ipt2socks") then
uci_set(uid, "type", "Socks") 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 if redirect == "1" then
uci_save() uci_save()
@@ -47,20 +47,23 @@ end
-- [[ ACLs Settings ]]-- -- [[ ACLs Settings ]]--
s = m:section(NamedSection, arg[1], translate("ACLs"), translate("ACLs")) 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 ---- Enable
o = s:option(Flag, "enabled", translate("Enable")) o = s:taboption("Main", Flag, "enabled", translate("Enable"))
o.default = 1 o.default = 1
o.rmempty = false o.rmempty = false
---- Remarks ---- Remarks
o = s:option(Value, "remarks", translate("Remarks")) o = s:taboption("Main", Value, "remarks", translate("Remarks"))
o.default = arg[1] o.default = arg[1]
o.rmempty = false o.rmempty = false
o = s:option(Value, "interface", translate("Source Interface")) o = s:taboption("Main", Value, "interface", translate("Source Interface"))
o:value("", translate("All")) o:value("", translate("All"))
local iface = api.get_network_devices() local iface = api.get_network_devices()
for _, d in ipairs(iface) do for _, d in ipairs(iface) do
@@ -94,7 +97,7 @@ table.sort(mac_t, function(a,b)
end) end)
---- Source ---- Source
sources = s:option(DynamicList, "sources", translate("Source")) sources = s:taboption("Main", DynamicList, "sources", translate("Source"))
sources.description = "<ul><li>" .. translate("Example:") sources.description = "<ul><li>" .. translate("Example:")
.. "</li><li>" .. translate("MAC") .. ": 00:00:00:FF:FF:FF" .. "</li><li>" .. translate("MAC") .. ": 00:00:00:FF:FF:FF"
.. "</li><li>" .. translate("IP") .. ": 192.168.1.100" .. "</li><li>" .. translate("IP") .. ": 192.168.1.100"
@@ -152,13 +155,14 @@ sources.validate = function(self, value, t)
return value return value
end 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("0", translate("No Proxy"))
o:value("1", translate("Proxy")) o:value("1", translate("Proxy"))
---- TCP No Redir Ports ---- TCP No Redir Ports
local TCP_NO_REDIR_PORTS = m:get("@global_forwarding[0]", "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("", translate("Use global config") .. "(" .. TCP_NO_REDIR_PORTS .. ")")
o:value("disable", translate("No patterns are used")) o:value("disable", translate("No patterns are used"))
o:value("1:65535", translate("All")) o:value("1:65535", translate("All"))
@@ -167,7 +171,7 @@ o.validate = port_validate
---- UDP No Redir Ports ---- UDP No Redir Ports
local UDP_NO_REDIR_PORTS = m:get("@global_forwarding[0]", "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'>" .. "<font color='red'>" ..
translate("Fill in the ports you don't want to be forwarded by the agent, with the highest priority.") .. translate("Fill in the ports you don't want to be forwarded by the agent, with the highest priority.") ..
"</font>") "</font>")
@@ -177,53 +181,48 @@ o:value("1:65535", translate("All"))
o:depends("mode", "1") o:depends("mode", "1")
o.validate = port_validate o.validate = port_validate
o = s:option(DummyValue, "_hide_node_option", "") o = s:taboption("Main", HideValue, "_hide_node_option", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o:depends("mode", "0") o:depends("mode", "0")
o:depends({ tcp_no_redir_ports = "1:65535", udp_no_redir_ports = "1:65535" }) 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 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 = "" }) o:depends({ tcp_no_redir_ports = "", udp_no_redir_ports = "" })
end 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.default = "0"
o.rmempty = false 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.group = {}
o:depends({ _hide_node_option = false, use_global_config = false }) o:depends({ _hide_node_option = false, use_global_config = false })
o.template = m:template_path("/cbi/nodes_listvalue") 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 end
current_node = current_node_id and m:get(current_node_id) or {} current_node = current_node_id and m:get(current_node_id) or {}
o = s:option(DummyValue, "_acl_node_bool", "") o = s:taboption("Main", HideValue, "node_save_before", "")
o.template = m:template_path("/cbi/hidevalue") o.value = current_node[".name"]
o.value = "1" o.cbid = function(self, section) return "node_save_before" end
o = s:taboption("Main", HideValue, "_acl_node_bool", "")
o:depends({ node = "", ['!reverse'] = true }) o:depends({ node = "", ['!reverse'] = true })
---- Log o = s:taboption("Main", HideValue, "_diff_global_node", "")
o = s:option(Flag, "log", translate("Enable Node Log")) if NODE ~= "" then
o.default = 0 o:depends({ node = NODE, ['!reverse'] = true })
o.rmempty = false else
o:depends({ _hide_node_option = false, use_global_config = false }) o:depends({ _acl_node_bool = "1" })
end
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")
---- TCP Proxy Drop Ports ---- TCP Proxy Drop Ports
local TCP_PROXY_DROP_PORTS = m:get("@global_forwarding[0]", "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("", translate("Use global config") .. "(" .. TCP_PROXY_DROP_PORTS .. ")")
o:value("disable", translate("No patterns are used")) o:value("disable", translate("No patterns are used"))
o.validate = port_validate o.validate = port_validate
@@ -232,7 +231,7 @@ o:depends({ _acl_node_bool = "1" })
---- UDP Proxy Drop Ports ---- UDP Proxy Drop Ports
local UDP_PROXY_DROP_PORTS = m:get("@global_forwarding[0]", "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("", translate("Use global config") .. "(" .. UDP_PROXY_DROP_PORTS .. ")")
o:value("disable", translate("No patterns are used")) o:value("disable", translate("No patterns are used"))
o:value("443", translate("QUIC")) o:value("443", translate("QUIC"))
@@ -242,7 +241,7 @@ o:depends({ _acl_node_bool = "1" })
---- TCP Redir Ports ---- TCP Redir Ports
local TCP_REDIR_PORTS = m:get("@global_forwarding[0]", "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("", translate("Use global config") .. "(" .. TCP_REDIR_PORTS .. ")")
o:value("1:65535", translate("All")) o:value("1:65535", translate("All"))
o:value("80,443", "80,443") o:value("80,443", "80,443")
@@ -254,7 +253,7 @@ o:depends({ _acl_node_bool = "1" })
---- UDP Redir Ports ---- UDP Redir Ports
local UDP_REDIR_PORTS = m:get("@global_forwarding[0]", "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("", translate("Use global config") .. "(" .. UDP_REDIR_PORTS .. ")")
o:value("1:65535", translate("All")) o:value("1:65535", translate("All"))
o:value("53", "53") o:value("53", "53")
@@ -262,7 +261,7 @@ o.validate = port_validate
o:depends({ use_global_config = true }) o:depends({ use_global_config = true })
o:depends({ _acl_node_bool = "1" }) o:depends({ _acl_node_bool = "1" })
o = s:option(DummyValue, "tips", " ") o = s:taboption("Proxy", DummyValue, "tips", " ")
o.rawhtml = true o.rawhtml = true
o.cfgvalue = function(t, n) o.cfgvalue = function(t, n)
return string.format('<font color="red">%s</font>', return string.format('<font color="red">%s</font>',
@@ -271,71 +270,67 @@ end
o:depends({ use_global_config = true }) o:depends({ use_global_config = true })
o:depends({ _acl_node_bool = "1" }) 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.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.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.default = "1"
o:depends({ _acl_node_bool = "1" }) o:depends({ _acl_node_bool = "1", _diff_global_node = "1" })
if has_gfwlist then 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.default = "1"
o:depends({ _acl_node_bool = "1" }) o:depends({ _acl_node_bool = "1", _diff_global_node = "1" })
end end
if has_chnlist or has_chnroute then 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("0", translate("Close(Not use)"))
o:value("direct", translate("Direct Connection")) o:value("direct", translate("Direct Connection"))
o:value("proxy", translate("Proxy")) o:value("proxy", translate("Proxy"))
o.default = "direct" o.default = "direct"
o:depends({ _acl_node_bool = "1" }) o:depends({ _acl_node_bool = "1", _diff_global_node = "1" })
end 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("disable", translate("No Proxy"))
o:value("proxy", translate("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("disable", translate("No Proxy"))
o:value("proxy", translate("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.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 -- Node → DNS Depends Settings
o = s:option(DummyValue, "_node_sel_shunt", "") o = s:taboption("Main", HideValue, "_node_sel_shunt", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o:depends({ node = "__always__" }) o:depends({ node = "__always__" })
o = s:option(DummyValue, "_node_sel_other", "") o = s:taboption("Main", HideValue, "_node_sel_other", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o:depends({ _node_sel_shunt = "1", ['!reverse'] = true }) o:depends({ _node_sel_shunt = "1", ['!reverse'] = true })
---- DNS ---- 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.default = "chinadns-ng"
o:value("dnsmasq", "Dnsmasq") o:value("dnsmasq", "Dnsmasq")
o:value("chinadns-ng", translate("ChinaDNS-NG (recommended)")) 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.default = "0"
o:depends({ _acl_node_bool = "1" }) o:depends({ _acl_node_bool = "1", _diff_global_node = "1" })
---- DNS Forward Mode ---- 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 if api.is_finded("dns2socks") then
o:value("dns2socks", "dns2socks") o:value("dns2socks", "dns2socks")
end end
@@ -345,7 +340,7 @@ end
if has_xray then if has_xray then
o:value("xray", "Xray") o:value("xray", "Xray")
end 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) o.write = function(self, section, value)
if value == "dns2socks" then if value == "dns2socks" then
m:del(section, "v2ray_dns_mode") m:del(section, "v2ray_dns_mode")
@@ -374,7 +369,7 @@ o.remove = function(self, section)
end end
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.default = "tcp"
o:value("tcp", "TCP") o:value("tcp", "TCP")
o:value("udp", "UDP") o:value("udp", "UDP")
@@ -389,7 +384,7 @@ o.write = function(self, section, value)
end end
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.default = "tcp"
o:value("tcp", "TCP") o:value("tcp", "TCP")
o:value("udp", "UDP") o:value("udp", "UDP")
@@ -408,7 +403,7 @@ o.write = function(self, section, value)
end end
---- DNS Forward ---- 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.datatype = "or(ipaddr,ipaddrport(1))"
o.default = "1.1.1.1" o.default = "1.1.1.1"
o:value("1.1.1.1", "1.1.1.1 (CloudFlare)") 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 = "tls"})
o:depends({singbox_dns_mode = "quic"}) 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.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.1/dns-query", "1.1.1.1 (CloudFlare)")
o:value("https://1.1.1.2/dns-query", "1.1.1.2 (CloudFlare-Security)") 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 = "doh"})
o:depends({singbox_dns_mode = "http3"}) 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 />" .. 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).") translate("This feature requires the DNS server to support the Edns Client Subnet (RFC7871).")
o.datatype = "ipaddr" o.datatype = "ipaddr"
o:depends({dns_mode = "sing-box"}) o:depends({dns_mode = "sing-box"})
o:depends({dns_mode = "xray"}) 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.default = "0"
o.rmempty = false o.rmempty = false
o:depends({dns_mode = "sing-box"}) o:depends({dns_mode = "sing-box"})
@@ -492,12 +487,12 @@ o.validate = function(self, value, t)
return value return value
end 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.datatype = "min(1)"
o.default = "30" o.default = "30"
o:depends({dns_mode = "sing-box"}) 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.default = "none"
o:value("gfw", translate("Remote DNS")) o:value("gfw", translate("Remote DNS"))
o:value("chn", translate("Direct DNS")) o:value("chn", translate("Direct DNS"))
@@ -515,18 +510,73 @@ o.description = desc
.. "</ul>" .. "</ul>"
o:depends({dns_shunt = "chinadns-ng", tcp_proxy_mode = "proxy", chn_list = "direct"}) 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.default = "0"
o.rmempty = false o.rmempty = false
o:depends({dns_shunt = "chinadns-ng"}) 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.default = "direct"
o:value("remote", translate("Remote DNS")) o:value("remote", translate("Remote DNS"))
o:value("direct", translate("Direct DNS")) o:value("direct", translate("Direct DNS"))
o.description = desc .. "</ul>" o.description = desc .. "</ul>"
o:depends({dns_shunt = "dnsmasq", tcp_proxy_mode = "proxy", chn_list = "direct"}) 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 o_node = s.fields["node"]
local shunt_list = {} local shunt_list = {}
@@ -536,7 +586,7 @@ for k, v in pairs(socks_list) do
end end
for k, v in pairs(nodes_table) do for k, v in pairs(nodes_table) do
if #normal_list == 0 then 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 break
end end
if v.protocol and v.protocol == "_shunt" then 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:value(v.id, v["remark"])
o_node.group[#o_node.group+1] = (v.group and v.group ~= "") and v.group or translate("default") 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 }) s.fields["_node_sel_shunt"]:depends({ node = v.id })
end end
if v.type == "sing-box" and has_singbox then if v.type == "sing-box" and has_singbox then
o_node:value(v.id, v["remark"]) 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") 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["_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 end
shunt_list[#shunt_list + 1] = v shunt_list[#shunt_list + 1] = v
else else
@@ -562,21 +612,17 @@ for k, v in pairs(nodes_table) do
end end
end end
m:appendTemplate("/acl/config_footer", {section = arg[1]})
--[[
-- Shunt -- 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") local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall/client/include/shunt_options.lua")
setfenv(shunt_lua, getfenv(1))(m, s, { setfenv(shunt_lua, getfenv(1))(m, s, {
s_cfgid = s.section,
node_id = current_node_id,
node = current_node, node = current_node,
verify_option = s.fields["node"] verify_option = s.fields["node"],
tab = "Shunt",
tab_desc = translate("Shunt Rule"),
}) })
end 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) return api.return_map(m)
@@ -20,7 +20,10 @@ for _, k in ipairs(com.order) do
if k ~= "chinadns-ng" then if k ~= "chinadns-ng" then
o = s:option(Value, k:gsub("%-","_") .. "_file", translatef("%s App Path", v.name)) o = s:option(Value, k:gsub("%-","_") .. "_file", translatef("%s App Path", v.name))
o.default = v.default_path or ("/usr/bin/" .. k) o.default = v.default_path or ("/usr/bin/" .. k)
o.rmempty = false o.placeholder = o.default
function o:remove(section)
self:write(section, self.default)
end
end end
end end
@@ -112,6 +112,10 @@ o:value("", translate("Close"))
o.group = {""} o.group = {""}
current_node_id = m:get(s.section, "node") 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 {} current_node = current_node_id and m:get(current_node_id) or {}
-- Shunt Start -- Shunt Start
@@ -120,17 +124,10 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
if current_node.protocol == "_shunt" then if current_node.protocol == "_shunt" then
local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall/client/include/shunt_options.lua") local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall/client/include/shunt_options.lua")
setfenv(shunt_lua, getfenv(1))(m, s, { setfenv(shunt_lua, getfenv(1))(m, s, {
s_cfgid = s.section,
node_id = current_node_id,
node = current_node, 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"], verify_option = s.fields["node"],
tab = "Shunt", tab = "Shunt",
tab_desc = translate("Shunt Rule") tab_desc = translate("Shunt Rule"),
}) })
end end
else else
@@ -165,20 +162,18 @@ o = s:taboption("Main", Flag, "node_socks_bind_local", translate("Node") .. " So
o.default = "1" o.default = "1"
o:depends("_node", "1") o:depends("_node", "1")
o = s:taboption("Main", DummyValue, "_node", "") o = s:taboption("Main", HideValue, "node_save_before", "")
o.template = m:template_path("/cbi/hidevalue") o.value = current_node[".name"]
o.value = "1" o.cbid = function(self, section) return "node_save_before" end
o = s:taboption("Main", HideValue, "_node", "")
o:depends({ node = "", ['!reverse'] = true }) o:depends({ node = "", ['!reverse'] = true })
-- Node → DNS Depends Settings -- Node → DNS Depends Settings
o = s:taboption("Main", DummyValue, "_node_sel_shunt", "") o = s:taboption("Main", HideValue, "_node_sel_shunt", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o:depends({ node = "__always__" }) o:depends({ node = "__always__" })
o = s:taboption("Main", DummyValue, "_node_sel_other", "") o = s:taboption("Main", HideValue, "_node_sel_other", "")
o.template = m:template_path("/cbi/hidevalue")
o.value = "1"
o:depends({ _node_sel_shunt = "1", ['!reverse'] = true }) o:depends({ _node_sel_shunt = "1", ['!reverse'] = true })
-- [[ DNS Settings ]]-- -- [[ DNS Settings ]]--
@@ -617,21 +612,46 @@ o:value("info", "Info")
o:value("warn", "Warning") o:value("warn", "Warning")
o:value("error", "Error") o:value("error", "Error")
o = s:taboption("log", Flag, "advanced_log_feature", translate("Advanced log feature"), translate("For professionals only.")) o = s:taboption("log", DummyValue, "_node_log", translate("Log File"))
o.default = "0" o.rawhtml = true
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.cfgvalue = function(t, n)
o:depends("advanced_log_feature", "1") local log_file = api.TMP_PATH .. "/acl/default/global.log"
o.default = "0" local log_url = api.url("get_redir_log") .. "?id=default"
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.")) local s = "<code>%s</code>&nbsp;&nbsp;" % log_file
o:depends({ ["advanced_log_feature"] = 1, ["sys_log"] = 0 }) if api.fs.access(log_file) then
o = s:taboption("log", Value, "log_event_filter", translate("Log Event Filter"), translate("Support regular expression.")) local btn = string.format(
o:depends("advanced_log_feature", "1") '<input class="btn cbi-button cbi-button-apply" type="button" value="%s" onclick="window.open(\'%s\', \'_blank\')" />',
o = s:taboption("log", Value, "log_event_cmd", translate("Shell Command"), translate("Shell command to execute, replace log content with %s.")) translate("View Log"),
o:depends("advanced_log_feature", "1") 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 = s:taboption("log", Flag, "log_chinadns_ng", translate("Enable") .. " ChinaDNS-NG " .. translate("Log"))
o.default = "0" o.default = "0"
o.rmempty = false 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 = s:taboption("log", DummyValue, "_log_tips", " ")
o.rawhtml = true o.rawhtml = true
@@ -647,6 +667,8 @@ s:tab("maintain", translate("Maintain"))
o = s:taboption("maintain", DummyValue, "") o = s:taboption("maintain", DummyValue, "")
o.template = m:template_path("/global/backup") 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 ]]-- -- [[ Socks Server ]]--
o = s:taboption("Main", Flag, "socks_enabled", "Socks " .. translate("Main switch")) o = s:taboption("Main", Flag, "socks_enabled", "Socks " .. translate("Main switch"))
o.rmempty = false o.rmempty = false
@@ -784,7 +806,7 @@ for k, v in pairs(nodes_table) do
end end
end end
m:appendTemplate("/global/footer", {shunt_list = api.jsonc.stringify(shunt_list)}) m:appendTemplate("/global/footer")
m:appendTemplate("/cbi/sortable", {sectiontype = s2.sectiontype}) m:appendTemplate("/cbi/sortable", {sectiontype = s2.sectiontype})
@@ -1,13 +1,28 @@
local m, s, data = ... 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 return
end end
local api = m.api local api = m.api
local s_cfgid = data.s_cfgid local section_id = s.section
local current_node_id = data.node_id 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 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 = {} local groups = {}
m:foreach("shunt_rules", function(s) m:foreach("shunt_rules", function(s)
@@ -16,6 +31,9 @@ m:foreach("shunt_rules", function(s)
end end
end) end)
local node_save_before = luci.http.formvalue("node_save_before")
local load_shunt = luci.http.formvalue("load_shunt")
local function get_cfgvalue() local function get_cfgvalue()
return function(self, section) return function(self, section)
return m:get(current_node_id, self.option) return m:get(current_node_id, self.option)
@@ -23,8 +41,10 @@ local function get_cfgvalue()
end end
local function get_write() local function get_write()
return function(self, section, value) return function(self, section, value)
if data.verify_option then if node_save_before and node_save_before ~= current_node_id then return end
if data.verify_option:formvalue(section) == current_node_id then 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) m:set(current_node_id, self.option, value)
end end
else else
@@ -34,8 +54,10 @@ local function get_write()
end end
local function get_remove() local function get_remove()
return function(self, section) return function(self, section)
if data.verify_option then if node_save_before and node_save_before ~= current_node_id then return end
if data.verify_option:formvalue(section) == current_node_id then 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) m:del(current_node_id, self.option)
end end
else else
@@ -44,10 +66,6 @@ local function get_remove()
end end
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 function add_option(class, option_name, option_title, option_desc)
local a local a
if data.tab then if data.tab then
@@ -63,8 +81,8 @@ local function add_option(class, option_name, option_title, option_desc)
a.write = get_write() a.write = get_write()
a.remove = get_remove() a.remove = get_remove()
end end
if data.verify_option then if verify_option then
a:depends(data.verify_option.option, current_node_id) a:depends(verify_option.option, current_node_id)
end end
return a return a
end end
@@ -81,7 +99,7 @@ local function add_depends(o, deps)
end end
end end
if data.node.type == "Xray" then if current_node.type == "Xray" then
o = add_option(ListValue, "domainStrategy", translate("Domain Strategy")) o = add_option(ListValue, "domainStrategy", translate("Domain Strategy"))
o:value("AsIs") o:value("AsIs")
o:value("IPIfNonMatch") o:value("IPIfNonMatch")
@@ -134,7 +152,7 @@ table.insert(shunt_rules, {
s2 = m:section(Table, shunt_rules, " ") s2 = m:section(Table, shunt_rules, " ")
s2.config = m.config s2.config = m.config
s2.sectiontype = "shunt_option_list" s2.sectiontype = shunt_section_type
o = s2:option(DummyValue, "remarks", translate("Rule")) o = s2:option(DummyValue, "remarks", translate("Rule"))
o.rawhtml = true 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"] return m:get(current_node_id, shunt_rules[section]["_node_option"]) or shunt_rules[section]["_node_default"]
end end
_node.write = function(self, section, value) _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) return m:set(current_node_id, shunt_rules[section]["_node_option"], value)
end end
_node.remove = function(self, section) _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"]) return m:del(current_node_id, shunt_rules[section]["_node_option"])
end end
@@ -170,9 +192,13 @@ o.cfgvalue = function(self, section)
return m:get(current_node_id, shunt_rules[section]["_fakedns_option"]) return m:get(current_node_id, shunt_rules[section]["_fakedns_option"])
end end
o.write = function(self, section, value) 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) return m:set(current_node_id, shunt_rules[section]["_fakedns_option"], value)
end end
o.remove = function(self, section) 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"]) return m:del(current_node_id, shunt_rules[section]["_fakedns_option"])
end end
@@ -186,9 +212,13 @@ proxy_tag_node.cfgvalue = function(self, section)
return m:get(current_node_id, shunt_rules[section]["_proxy_tag_option"]) return m:get(current_node_id, shunt_rules[section]["_proxy_tag_option"])
end end
proxy_tag_node.write = function(self, section, value) 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) return m:set(current_node_id, shunt_rules[section]["_proxy_tag_option"], value)
end end
proxy_tag_node.remove = function(self, section) 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"]) return m:del(current_node_id, shunt_rules[section]["_proxy_tag_option"])
end end
@@ -204,4 +234,11 @@ for k1, v1 in pairs(node_list) do
end end
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/" local types_dir = "/usr/lib/lua/luci/model/cbi/" .. api.appname .. "/client/type/"
s.val = {} s.val = {}
s.val["type"] = m:get(arg[1], "type") s.val["type"] = m:get(arg[1], "type")
s.val["protocol"] = m:get(arg[1], "protocol")
if luci.http.formvalue("cbi.submit") == "1" then if luci.http.formvalue("cbi.submit") == "1" then
local formvalue_type = luci.http.formvalue(formvalue_key .. "type") local formvalue_type = luci.http.formvalue(formvalue_key .. "type")
@@ -72,34 +71,6 @@ end
o = s:option(ListValue, "type", translate("Type")) 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 = {} local type_table = {}
for filename in api.fs.dir(types_dir) do for filename in api.fs.dir(types_dir) do
table.insert(type_table, filename) 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 return
end 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.type_name = type_name
s.option_prefix = "singbox_" s.option_prefix = "singbox_"
api.set_type_cbi(s)
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 ""
local ss_method_new_list = { 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" "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("_urltest", translate("URLTest"))
o:value("_shunt", translate("Shunt")) o:value("_shunt", translate("Shunt"))
o:value("_iface", translate("Custom Interface")) o:value("_iface", translate("Custom Interface"))
function o.custom_cfgvalue(self, section)
if arg_select_proto ~= "" then local protocol_val = m:get(s.section, "protocol")
return arg_select_proto local formvalue_proto = s.fields["protocol"]:formvalue(s.section)
else if formvalue_proto then
return m:get(section, self.config_option) protocol_val = formvalue_proto
end
end end
local load_urltest_options = s1.val["protocol"] == "_urltest" or arg_select_proto == "_urltest" local load_urltest_options = protocol_val == "_urltest"
local load_shunt_options = s1.val["protocol"] == "_shunt" or arg_select_proto == "_shunt" local load_shunt_options = protocol_val == "_shunt"
local load_iface_options = s1.val["protocol"] == "_iface" or arg_select_proto == "_iface" local load_iface_options = protocol_val == "_iface"
local load_normal_options = true local load_normal_options = true
if load_urltest_options or load_shunt_options or load_iface_options then if load_urltest_options or load_shunt_options or load_iface_options then
load_normal_options = nil load_normal_options = nil
end end
if not arg_select_proto:find("_") then
load_normal_options = true
end
local netdev_list = api.get_network_devices() local netdev_list = api.get_network_devices()
local node_list = api.get_node_list() local node_list = api.get_node_list()
@@ -115,11 +107,11 @@ if load_urltest_options then -- [[ URLTest Start ]]
end end
end end
-- 读取旧 DynamicList -- 读取旧 DynamicList
function o.custom_cfgvalue(self, section) function o.cfgvalue(self, section)
return table.concat(m:get(section, "urltest_node") or {}, " ") return table.concat(m:get(section, "urltest_node") or {}, " ")
end end
-- 写入保持 DynamicList -- 写入保持 DynamicList
function o.custom_write(self, section, value) function o.write(self, section, value)
local old = m:get(section, "urltest_node") or {} local old = m:get(section, "urltest_node") or {}
local new, set = {}, {} local new, set = {}, {}
for v in value:gmatch("%S+") do 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 = "shadowsocks", tls = true })
o:depends({ protocol = "trojan", tls = true }) o:depends({ protocol = "trojan", tls = true })
o:depends({ protocol = "anytls", tls = true }) o:depends({ protocol = "anytls", tls = true })
o:depends({ protocol = "http", tls = true })
o = s:option(Value, "reality_publicKey", translate("Public Key")) o = s:option(Value, "reality_publicKey", translate("Public Key"))
o:depends({ reality = true }) o:depends({ reality = true })
@@ -714,16 +707,6 @@ o = s:option(Value, "ws_path", translate("WebSocket Path"))
o.placeholder = "/" o.placeholder = "/"
o:depends({ transport = "ws" }) o:depends({ transport = "ws" })
o = s:option(Flag, "ws_enableEarlyData", translate("Enable early data"))
o:depends({ transport = "ws" })
o = s:option(Value, "ws_maxEarlyData", translate("Early data length"))
o.default = "1024"
o:depends({ ws_enableEarlyData = true })
o = s:option(Value, "ws_earlyDataHeaderName", translate("Early data header name"), translate("Recommended value: Sec-WebSocket-Protocol"))
o:depends({ ws_enableEarlyData = true })
-- [[ HTTPUpgrade部分 ]]-- -- [[ HTTPUpgrade部分 ]]--
o = s:option(Value, "httpupgrade_host", translate("HTTPUpgrade Host")) o = s:option(Value, "httpupgrade_host", translate("HTTPUpgrade Host"))
o:depends({ transport = "httpupgrade" }) o:depends({ transport = "httpupgrade" })
@@ -935,7 +918,7 @@ if not load_shunt_options then
for k1, v1 in pairs(node_list) do for k1, v1 in pairs(node_list) do
if k1 ~= "shunt_list" and k1 ~= "iface_list" then if k1 ~= "shunt_list" and k1 ~= "iface_list" then
for i, v in ipairs(v1) do 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:value(v.id, v.remark)
o1.group[#o1.group+1] = (v.group and v.group ~= "") and v.group or translate("default") o1.group[#o1.group+1] = (v.group and v.group ~= "") and v.group or translate("default")
if k1 == "normal_list" then if k1 == "normal_list" then
@@ -949,14 +932,15 @@ if not load_shunt_options then
end end
end end
api.luci_types(s1, s) api.type_cbi_section(s1, s)
if load_shunt_options then 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") local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall/client/include/shunt_options.lua")
setfenv(shunt_lua, getfenv(1))(m, s1, { setfenv(shunt_lua, getfenv(1))(m, s1, {
node_id = arg[1], node = {
node = current_node, [".name"] = s.section,
type = type_name,
},
node_list = node_list, node_list = node_list,
}) })
end end
@@ -15,15 +15,11 @@ if s1.val["type"] ~= type_name then
return return
end 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.type_name = type_name
s.option_prefix = "xray_" s.option_prefix = "xray_"
api.set_type_cbi(s)
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 ""
local ss_method_list = { 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" "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 end
o:value("_shunt", translate("Shunt")) o:value("_shunt", translate("Shunt"))
o:value("_iface", translate("Custom Interface")) o:value("_iface", translate("Custom Interface"))
function o.custom_cfgvalue(self, section) local protocol_val = m:get(s.section, "protocol")
if arg_select_proto ~= "" then local formvalue_proto = s.fields["protocol"]:formvalue(s.section)
return arg_select_proto if formvalue_proto then
else protocol_val = formvalue_proto
return m:get(section, self.config_option)
end
end end
local load_balancing_options = s1.val["protocol"] == "_balancing" or arg_select_proto == "_balancing" local load_balancing_options = protocol_val == "_balancing"
local load_shunt_options = s1.val["protocol"] == "_shunt" or arg_select_proto == "_shunt" local load_shunt_options = protocol_val == "_shunt"
local load_iface_options = s1.val["protocol"] == "_iface" or arg_select_proto == "_iface" local load_iface_options = protocol_val == "_iface"
local load_normal_options = true local load_normal_options = true
if load_balancing_options or load_shunt_options or load_iface_options then if load_balancing_options or load_shunt_options or load_iface_options then
load_normal_options = nil load_normal_options = nil
end end
if not arg_select_proto:find("_") then
load_normal_options = true
end
local netdev_list = api.get_network_devices() local netdev_list = api.get_network_devices()
local node_list = api.get_node_list() local node_list = api.get_node_list()
local fallback_list = {} local fallback_list = {}
local is_balancer = nil local is_balancer = nil
for k, e in ipairs(node_list.balancing_list or {}) do 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] = { fallback_list[#fallback_list + 1] = {
id = e["id"], id = e["id"],
remark = e["remark"], remark = e["remark"],
@@ -110,11 +101,11 @@ if load_balancing_options then -- [[ Load balancing Start ]]
end end
end end
-- 读取旧 DynamicList -- 读取旧 DynamicList
function o.custom_cfgvalue(self, section) function o.cfgvalue(self, section)
return table.concat(m:get(section, "balancing_node") or {}, " ") return table.concat(m:get(section, "balancing_node") or {}, " ")
end end
-- 写入保持 DynamicList -- 写入保持 DynamicList
function o.custom_write(self, section, value) function o.write(self, section, value)
local old = m:get(section, "balancing_node") or {} local old = m:get(section, "balancing_node") or {}
local new, set = {}, {} local new, set = {}, {}
for v in value:gmatch("%S+") do 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) local depth = get_fallback_depth(v.id)
-- 超过最大套娃层数后,不允许继续选择 balancer -- 超过最大套娃层数后,不允许继续选择 balancer
if depth < MAX_FALLBACK_DEPTH if depth < MAX_FALLBACK_DEPTH
and not will_loop(arg[1], v.id) and not will_loop(s.section, v.id)
then then
o:value(v.id, v.remark) o:value(v.id, v.remark)
o.group[#o.group + 1] = (v.group and v.group ~= "") and v.group or translate("default") 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.default = 0
o:depends({ protocol = "vmess" }) o:depends({ protocol = "vmess" })
o:depends({ protocol = "vless" }) o:depends({ protocol = "vless" })
o:depends({ protocol = "http" })
o:depends({ protocol = "trojan" }) o:depends({ protocol = "trojan" })
o:depends({ protocol = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
@@ -678,13 +670,13 @@ o.validate = function(self, value)
if v then return v end if v then return v end
return nil, "XHTTP Extra " .. translate("Must be JSON text!") return nil, "XHTTP Extra " .. translate("Must be JSON text!")
end end
o.custom_cfgvalue = function(self, section, value) o.cfgvalue = function(self, section, value)
local raw = m:get(section, "xhttp_extra") local raw = m:get(section, "xhttp_extra")
if raw then if raw then
return api.base64Decode(raw) return api.base64Decode(raw)
end end
end end
o.custom_write = function(self, section, value) o.write = function(self, section, value)
m:set(section, "xhttp_extra", api.base64Encode(value) or "") m:set(section, "xhttp_extra", api.base64Encode(value) or "")
local success, data = pcall(api.jsonc.parse, value) local success, data = pcall(api.jsonc.parse, value)
if success and data then if success and data then
@@ -700,7 +692,7 @@ o.custom_write = function(self, section, value)
m:del(section, "download_address") m:del(section, "download_address")
end end
end end
o.custom_remove = function(self, section, value) o.remove = function(self, section, value)
m:del(section, "xhttp_extra") m:del(section, "xhttp_extra")
m:del(section, "download_address") m:del(section, "download_address")
end end
@@ -728,6 +720,8 @@ o:depends({ protocol = "vless", transport = "raw" })
o:depends({ protocol = "vless", transport = "ws" }) o:depends({ protocol = "vless", transport = "ws" })
o:depends({ protocol = "vless", transport = "grpc" }) o:depends({ protocol = "vless", transport = "grpc" })
o:depends({ protocol = "vless", transport = "httpupgrade" }) o:depends({ protocol = "vless", transport = "httpupgrade" })
o:depends({ protocol = "http" })
o:depends({ protocol = "socks" })
o:depends({ protocol = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
o:depends({ protocol = "trojan" }) o:depends({ protocol = "trojan" })
@@ -763,13 +757,13 @@ o.validate = function(self, value)
if v then return v end if v then return v end
return nil, "FinalMask " .. translate("Must be JSON text!") return nil, "FinalMask " .. translate("Must be JSON text!")
end end
o.custom_cfgvalue = function(self, section, value) o.cfgvalue = function(self, section, value)
local raw = m:get(section, "finalmask") local raw = m:get(section, "finalmask")
if raw then if raw then
return api.base64Decode(raw) return api.base64Decode(raw)
end end
end end
o.custom_write = function(self, section, value) o.write = function(self, section, value)
m:set(section, "finalmask", api.base64Encode(value) or "") m:set(section, "finalmask", api.base64Encode(value) or "")
end end
@@ -871,7 +865,7 @@ if not load_shunt_options then
for k1, v1 in pairs(node_list) do for k1, v1 in pairs(node_list) do
if k1 ~= "shunt_list" and k1 ~= "iface_list" then if k1 ~= "shunt_list" and k1 ~= "iface_list" then
for i, v in ipairs(v1) do 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:value(v.id, v.remark)
o1.group[#o1.group+1] = (v.group and v.group ~= "") and v.group or translate("default") o1.group[#o1.group+1] = (v.group and v.group ~= "") and v.group or translate("default")
if k1 == "normal_list" then if k1 == "normal_list" then
@@ -885,14 +879,16 @@ if not load_shunt_options then
end end
end end
api.luci_types(s1, s) api.type_cbi_section(s1, s)
if load_shunt_options then if load_shunt_options then
local current_node = m:get(arg[1]) or {} local current_node = m:get(arg[1]) or {}
local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall/client/include/shunt_options.lua") local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall/client/include/shunt_options.lua")
setfenv(shunt_lua, getfenv(1))(m, s1, { setfenv(shunt_lua, getfenv(1))(m, s1, {
node_id = arg[1], node = {
node = current_node, [".name"] = s.section,
type = type_name,
},
node_list = node_list, node_list = node_list,
}) })
end end
@@ -12,9 +12,11 @@ if s1.val["type"] ~= type_name then
return return
end 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.type_name = type_name
s.option_prefix = "ssrust_" s.option_prefix = "ssrust_"
api.set_type_cbi(s)
local ssrust_encrypt_method_list = { local ssrust_encrypt_method_list = {
"none", "plain", "none", "plain",
@@ -69,4 +71,4 @@ end
o = s:option(Value, "plugin_opts", translate("opts")) o = s:option(Value, "plugin_opts", translate("opts"))
o:depends({ plugin_enabled = true }) 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 return
end 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.type_name = type_name
s.option_prefix = "ssr_" s.option_prefix = "ssr_"
api.set_type_cbi(s)
local ssr_encrypt_method_list = { local ssr_encrypt_method_list = {
"none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb", "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 = s:option(Flag, "tcp_fast_open", "TCP " .. translate("Fast Open"), translate("Need node support required"))
o.default = 0 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 return
end 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.type_name = type_name
s.option_prefix = "hysteria2_" s.option_prefix = "hysteria2_"
api.set_type_cbi(s)
local function _n(name)
return s.option_prefix .. name
end
o = s:option(ListValue, "protocol", translate("Protocol")) o = s:option(ListValue, "protocol", translate("Protocol"))
o:value("udp", "UDP") o:value("udp", "UDP")
@@ -32,22 +30,22 @@ o:depends({ realms = false })
o = s:option(Value, "hop", translate("Port hopping range")) 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.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: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 = 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.datatype = "or(uinteger,portrange)"
o.placeholder = "30" o.placeholder = "30"
o.default = "30" o.default = "30"
o.rewrite_option = _n(o.option) o.rewrite_option = o.option
o:depends({ realms = false }) o:depends({ realms = false })
o = s:option(Flag, "realms", translate("Realms")) o = s:option(Flag, "realms", translate("Realms"))
o.default = "0" 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 = 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:depends({ realms = "1" })
o.validate = function(self, value) o.validate = function(self, value)
value = api.trim(value) value = api.trim(value)
@@ -58,26 +56,26 @@ end
o = s:option(DynamicList, "realm_stun", translate("Realm STUN")) 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.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: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 = 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.default = "0"
o.rewrite_option = _n(o.option) o.rewrite_option = o.option
o:depends({ realms = "1" }) o:depends({ realms = "1" })
o = s:option(Value, "auth_password", translate("Auth Password")) o = s:option(Value, "auth_password", translate("Auth Password"))
o.password = true o.password = true
o.rewrite_option = _n(o.option) o.rewrite_option = o.option
o = s:option(ListValue, "obfs_type", translate("Obfs Type")) o = s:option(ListValue, "obfs_type", translate("Obfs Type"))
o:value("", translate("Disable")) o:value("", translate("Disable"))
o:value("salamander") o:value("salamander")
o:value("gecko") o:value("gecko")
o.rewrite_option = _n(o.option) o.rewrite_option = o.option
o = s:option(Value, "obfs_password", translate("Obfs Password")) 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 = "salamander" })
o:depends({ obfs_type = "gecko" }) o:depends({ obfs_type = "gecko" })
@@ -86,14 +84,14 @@ o.datatype = "uinteger"
o.placeholder = "512" o.placeholder = "512"
o.default = "512" o.default = "512"
o:depends({ obfs_type = "gecko" }) 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 = s:option(Value, "obfs_MaxPacketSize", translate("Gecko Packet Size (max)"))
o.datatype = "uinteger" o.datatype = "uinteger"
o.placeholder = "1200" o.placeholder = "1200"
o.default = "1200" o.default = "1200"
o:depends({ obfs_type = "gecko" }) 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 = s:option(Flag, "fast_open", translate("Fast Open"))
o.default = "0" 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, "tls_pinSHA256", translate("PinSHA256"),translate("Certificate fingerprint"))
o = s:option(Value, "up_mbps", translate("Max upload Mbps")) 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 = 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 = 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 = 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 = s:option(Value, "idle_timeout", translate("Idle Timeout"), translate("Units:seconds") .. " (4~120)")
o.datatype = "range(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 = s:option(Value, "keep_alive_period", translate("QUIC KeepAlive interval"), translate("Units:seconds") .. " (2~60)")
o.datatype = "range(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 = s:option(Flag, "disable_mtu_discovery", translate("Disable MTU detection"))
o.default = "0" o.default = "0"
o.rewrite_option = _n(o.option) o.rewrite_option = o.option
o = s:option(Flag, "ech", translate("ECH")) o = s:option(Flag, "ech", translate("ECH"))
@@ -145,6 +143,6 @@ end
o = s:option(Flag, "lazy_start", translate("Lazy Start")) o = s:option(Flag, "lazy_start", translate("Lazy Start"))
o.default = "0" 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 return
end 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.type_name = type_name
s.option_prefix = "naive_" s.option_prefix = "naive_"
api.set_type_cbi(s)
o = s:option(ListValue, "protocol", translate("Protocol")) o = s:option(ListValue, "protocol", translate("Protocol"))
o:value("https", translate("HTTPS")) o:value("https", translate("HTTPS"))
@@ -30,4 +32,4 @@ o = s:option(Value, "username", translate("Username"))
o = s:option(Value, "password", translate("Password")) o = s:option(Value, "password", translate("Password"))
o.password = true 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 return
end 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.type_name = type_name
s.option_prefix = "socks_" s.option_prefix = "socks_"
api.set_type_cbi(s)
o = s:option(Value, "port", translate("Listen Port")) o = s:option(Value, "port", translate("Listen Port"))
o.datatype = "port" o.datatype = "port"
@@ -44,4 +46,4 @@ o:depends({ firewall_allow = true })
o = s:option(Flag, "log", translate("Log")) o = s:option(Flag, "log", translate("Log"))
o.default = "1" 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 return
end 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.type_name = type_name
s.option_prefix = "singbox_" s.option_prefix = "singbox_"
api.set_type_cbi(s)
local singbox_tags = luci.sys.exec(singbox_bin .. " version | grep 'Tags:' | awk '{print $2}'") 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 if v then return v end
return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!") return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!")
end end
o.custom_cfgvalue = function(self, section, value) o.cfgvalue = function(self, section, value)
local config_str = m:get(section, "config_str") local config_str = m:get(section, "config_str")
if config_str then if config_str then
return api.base64Decode(config_str) return api.base64Decode(config_str)
end end
end end
o.custom_write = function(self, section, value) o.write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "") m:set(section, "config_str", api.base64Encode(value) or "")
end end
@@ -397,6 +399,10 @@ o:depends({ transport = "ws" })
o = s:option(Value, "ws_path", translate("WebSocket Path")) o = s:option(Value, "ws_path", translate("WebSocket Path"))
o:depends({ transport = "ws" }) o:depends({ transport = "ws" })
o = s:option(Value, "ws_earlyDataHeaderName", translate("Early data header name"), translate("Recommended value: Sec-WebSocket-Protocol"))
o.placeholder = "Sec-WebSocket-Protocol"
o:depends({ transport = "ws" })
-- [[ HTTPUpgrade部分 ]]-- -- [[ HTTPUpgrade部分 ]]--
o = s:option(Value, "httpupgrade_host", translate("HTTPUpgrade Host")) o = s:option(Value, "httpupgrade_host", translate("HTTPUpgrade Host"))
@@ -562,4 +568,4 @@ o:value("warn")
o:value("error") o:value("error")
o:depends({ log = true }) 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 return
end 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.type_name = type_name
s.option_prefix = "xray_" s.option_prefix = "xray_"
api.set_type_cbi(s)
local ss_method_list = { 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" "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 if v then return v end
return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!") return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!")
end end
o.custom_cfgvalue = function(self, section, value) o.cfgvalue = function(self, section, value)
local config_str = m:get(section, "config_str") local config_str = m:get(section, "config_str")
if config_str then if config_str then
return api.base64Decode(config_str) return api.base64Decode(config_str)
end end
end end
o.custom_write = function(self, section, value) o.write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "") m:set(section, "config_str", api.base64Encode(value) or "")
end end
@@ -204,13 +206,18 @@ o.validate = function(self, value, t)
end end
o:depends({ protocol = "vmess" }) o:depends({ protocol = "vmess" })
o:depends({ protocol = "vless" }) o:depends({ protocol = "vless" })
o:depends({ protocol = "http" })
o:depends({ protocol = "shadowsocks" }) o:depends({ protocol = "shadowsocks" })
o:depends({ protocol = "trojan" }) o:depends({ protocol = "trojan" })
-- [[ REALITY部分 ]] -- -- [[ REALITY部分 ]] --
o = s:option(Flag, "reality", translate("REALITY")) o = s:option(Flag, "reality", translate("REALITY"))
o.default = 0 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 = s:option(Value, "reality_private_key", translate("Private Key"))
o:depends({ reality = true }) o:depends({ reality = true })
@@ -257,8 +264,8 @@ end
-- [[ TLS部分 ]] -- -- [[ TLS部分 ]] --
o = s:option(FileUpload, "tls_certificateFile", translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem") 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" o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. s.section .. ".pem"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end if o and o:formvalue(s.section) then o.default = o:formvalue(s.section) end
o:depends({ tls = true, reality = false }) o:depends({ tls = true, reality = false })
o:depends({ protocol = "hysteria2"}) o:depends({ protocol = "hysteria2"})
o.validate = function(self, value, t) o.validate = function(self, value, t)
@@ -273,8 +280,8 @@ o.validate = function(self, value, t)
end end
o = s:option(FileUpload, "tls_keyFile", translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key") 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" o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. s.section .. ".key"
if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end if o and o:formvalue(s.section) then o.default = o:formvalue(s.section) end
o:depends({ tls = true, reality = false }) o:depends({ tls = true, reality = false })
o:depends({ protocol = "hysteria2"}) o:depends({ protocol = "hysteria2"})
o.validate = function(self, value, t) o.validate = function(self, value, t)
@@ -396,13 +403,13 @@ o:depends({ use_finalmask = true })
o.rows = 10 o.rows = 10
o.wrap = "off" o.wrap = "off"
o.datatype = "json" o.datatype = "json"
o.custom_cfgvalue = function(self, section, value) o.cfgvalue = function(self, section, value)
local raw = m:get(section, "finalmask") local raw = m:get(section, "finalmask")
if raw then if raw then
return api.base64Decode(raw) return api.base64Decode(raw)
end end
end end
o.custom_write = function(self, section, value) o.write = function(self, section, value)
m:set(section, "finalmask", api.base64Encode(value) or "") m:set(section, "finalmask", api.base64Encode(value) or "")
end end
@@ -549,4 +556,4 @@ o:value("warning")
o:value("error") o:value("error")
o:depends({ log = true }) 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 return
end 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.type_name = type_name
s.option_prefix = "ssrust_" s.option_prefix = "ssrust_"
api.set_type_cbi(s)
local ssrust_encrypt_method_list = { local ssrust_encrypt_method_list = {
"plain", "none", "plain", "none",
@@ -39,13 +41,13 @@ o.validate = function(self, value)
if v then return v end if v then return v end
return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!") return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!")
end end
o.custom_cfgvalue = function(self, section, value) o.cfgvalue = function(self, section, value)
local config_str = m:get(section, "config_str") local config_str = m:get(section, "config_str")
if config_str then if config_str then
return api.base64Decode(config_str) return api.base64Decode(config_str)
end end
end end
o.custom_write = function(self, section, value) o.write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "") m:set(section, "config_str", api.base64Encode(value) or "")
end end
@@ -88,4 +90,4 @@ o = s:option(Flag, "log", translate("Log"))
o.default = "1" o.default = "1"
o.rmempty = false 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 return
end 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.type_name = type_name
s.option_prefix = "ssr_" s.option_prefix = "ssr_"
api.set_type_cbi(s)
local ssr_encrypt_method_list = { local ssr_encrypt_method_list = {
"none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb", "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 if v then return v end
return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!") return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!")
end end
o.custom_cfgvalue = function(self, section, value) o.cfgvalue = function(self, section, value)
local config_str = m:get(section, "config_str") local config_str = m:get(section, "config_str")
if config_str then if config_str then
return api.base64Decode(config_str) return api.base64Decode(config_str)
end end
end end
o.custom_write = function(self, section, value) o.write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "") m:set(section, "config_str", api.base64Encode(value) or "")
end end
@@ -119,4 +121,4 @@ o = s:option(Flag, "log", translate("Log"))
o.default = "1" o.default = "1"
o.rmempty = false 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 return
end 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.type_name = type_name
s.option_prefix = "hysteria2_" s.option_prefix = "hysteria2_"
api.set_type_cbi(s)
local function _n(name)
return s.option_prefix .. name
end
o = s:option(Flag, "custom", translate("Use Custom Config")) o = s:option(Flag, "custom", translate("Use Custom Config"))
@@ -33,13 +31,13 @@ o.validate = function(self, value)
if v then return v end if v then return v end
return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!") return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!")
end end
o.custom_cfgvalue = function(self, section, value) o.cfgvalue = function(self, section, value)
local config_str = m:get(section, "config_str") local config_str = m:get(section, "config_str")
if config_str then if config_str then
return api.base64Decode(config_str) return api.base64Decode(config_str)
end end
end end
o.custom_write = function(self, section, value) o.write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "") m:set(section, "config_str", api.base64Encode(value) or "")
end end
@@ -55,11 +53,11 @@ o:depends({ custom = false })
o = s:option(Flag, "realms", translate("Realms")) o = s:option(Flag, "realms", translate("Realms"))
o.default = "0" o.default = "0"
o.rewrite_option = _n(o.option) o.rewrite_option = o.option
o:depends({ custom = false }) o:depends({ custom = false })
o = s:option(Value, "realm_url", translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name") 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:depends({ realms = "1" })
o.validate = function(self, value) o.validate = function(self, value)
value = api.trim(value) value = api.trim(value)
@@ -70,23 +68,23 @@ end
o = s:option(DynamicList, "realm_stun", translate("Realm STUN")) 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.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: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 = 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.default = "0"
o.rewrite_option = _n(o.option) o.rewrite_option = o.option
o:depends({ realms = "1" }) o:depends({ realms = "1" })
o = s:option(ListValue, "obfs_type", translate("Obfs Type")) o = s:option(ListValue, "obfs_type", translate("Obfs Type"))
o:value("", translate("Disable")) o:value("", translate("Disable"))
o:value("salamander") o:value("salamander")
o:value("gecko") o:value("gecko")
o.rewrite_option = _n(o.option) o.rewrite_option = o.option
o:depends({ custom = false }) o:depends({ custom = false })
o = s:option(Value, "obfs_password", translate("Obfs Password")) 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 = "salamander" })
o:depends({ obfs_type = "gecko" }) o:depends({ obfs_type = "gecko" })
@@ -95,31 +93,31 @@ o.datatype = "uinteger"
o.placeholder = "512" o.placeholder = "512"
o.default = "512" o.default = "512"
o:depends({ obfs_type = "gecko" }) 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 = s:option(Value, "obfs_MaxPacketSize", translate("Gecko Packet Size (max)"))
o.datatype = "uinteger" o.datatype = "uinteger"
o.placeholder = "1200" o.placeholder = "1200"
o.default = "1200" o.default = "1200"
o:depends({ obfs_type = "gecko" }) o:depends({ obfs_type = "gecko" })
o.rewrite_option = _n(o.option) o.rewrite_option = o.option
o = s:option(Flag, "udp", translate("UDP")) o = s:option(Flag, "udp", translate("UDP"))
o.default = "1" o.default = "1"
o.rewrite_option = _n(o.option) o.rewrite_option = o.option
o:depends({ custom = false }) o:depends({ custom = false })
o = s:option(Value, "up_mbps", translate("Max upload Mbps")) 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:depends({ custom = false })
o = s:option(Value, "down_mbps", translate("Max download Mbps")) 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:depends({ custom = false })
o = s:option(Flag, "ignoreClientBandwidth", translate("ignoreClientBandwidth")) o = s:option(Flag, "ignoreClientBandwidth", translate("ignoreClientBandwidth"))
o.default = "0" o.default = "0"
o.rewrite_option = _n(o.option) o.rewrite_option = o.option
o:depends({ custom = false }) o:depends({ custom = false })
o = s:option(FileUpload, "tls_certificateFile", translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem") 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.default = "1"
o.rmempty = false o.rmempty = false
api.luci_types(s1, s) api.type_cbi_section(s1, s)
+110 -94
View File
@@ -163,12 +163,16 @@ function sh_uci_commit(config)
exec_call(string.format("uci -q commit %s", config)) exec_call(string.format("uci -q commit %s", config))
end 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) 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 end
function get_cache_var(key) 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 if val == "" then val = nil end
return val return val
end end
@@ -1378,11 +1382,22 @@ function to_move(app_name,file)
end end
function get_version() function get_version()
local version = sys.exec("opkg list-installed luci-app-passwall 2>/dev/null | awk '{print $3}'") local version
if not version or #version == 0 then 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-") version = sys.exec("apk list luci-app-passwall 2>/dev/null | awk '/installed/ {print $1}' | cut -d'-' -f4-")
end end
return (version or ""):gsub("\n", ""):match("^([^-]+)") version = (version or ""):match("^%s*(.-)%s*$")
if version ~= "" then
fs.writefile(version_file, version)
end
end
return version:match("^([^-]+)") or ""
end end
function to_check_self() function to_check_self()
@@ -1506,61 +1521,35 @@ function set_default_cbi()
return cbi.AbstractValue.write(self, section, new_val) return cbi.AbstractValue.write(self, section, new_val)
end end
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 end
function return_map(map) function set_type_cbi(s)
local cbi = require "luci.cbi" local cbi = require "luci.cbi"
local api = require "luci.passwall.api" local s1 = s.parent
if true then function s.option(s_self, class, option, ...)
-- header local obj = class(s_self.map, s_self, option, ...)
local header = cbi.Template(appname .. "/cbi/header") obj.config_option = option
header.map = map obj.option_prefix = s_self.option_prefix
table.insert(map.children, 1, header) obj.option = s_self.option_prefix .. option
end obj.cfgvalue = function(self, section)
if true then
-- footer
local footer = cbi.Template(appname .. "/cbi/footer")
footer.map = map
map:append(footer)
end
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 if self.rewrite_option then
return m:get(section, self.rewrite_option) return self.map:get(section, self.rewrite_option)
else else
return m:get(section, self.config_option) return self.map:get(section, self.config_option)
end end
end end
end obj.write = function(self, section, value)
o.write = function(self, section, value) if s1.fields["type"]:formvalue(s_self.section) == s_self.type_name then
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 local new_val = value
if util.instanceof(self, cbi.DynamicList) then if util.instanceof(self, cbi.DynamicList) then
local new_t = {} local new_t = {}
@@ -1576,63 +1565,82 @@ function luci_types(s, s2)
end end
end end
if self.rewrite_option then if self.rewrite_option then
m:set(section, self.rewrite_option, new_val) self.map:set(section, self.rewrite_option, new_val)
else else
m:set(section, self.config_option, new_val) self.map:set(section, self.config_option, new_val)
end end
end end
end end
end obj.remove = function(self, section)
o.remove = function(self, section) if s1.fields["type"]:formvalue(s_self.section) == s_self.type_name then
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 if self.rewrite_option then
m:del(section, self.rewrite_option) self.map:del(section, self.rewrite_option)
else else
m:del(section, self.config_option) self.map:del(section, self.config_option)
end end
end 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
end end
local deps = o.deps function type_cbi_section(s, s2)
if #deps > 0 then for i, v in ipairs(s2.children) do
local function process_deps(dep) local o = s2.children[i]
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) s:append(o)
s.fields[o.option] = o
end end
end end
function return_map(map)
local cbi = require "luci.cbi"
if true then
-- header
local header = cbi.Template(appname .. "/cbi/header")
header.map = map
table.insert(map.children, 1, header)
end
if true then
-- footer
local footer = cbi.Template(appname .. "/cbi/footer")
footer.map = map
map:append(footer)
end
return map
end
function get_std_domain(domain) function get_std_domain(domain)
domain = trim(domain) domain = trim(domain)
if domain == "" then return "" end if domain == "" then return "" end
@@ -2068,3 +2076,11 @@ function gen_wireguard_key()
} }
end end
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,25 +93,39 @@ function gen_outbound(flag, node, tag, proxy_table)
end end
local remarks = node.remarks local remarks = node.remarks
local proxy_tag = nil local proxy_tag, fragment, record_fragment
local fragment = nil
local record_fragment = nil
local run_socks_instance = true
if proxy_table ~= nil and type(proxy_table) == "table" then if proxy_table ~= nil and type(proxy_table) == "table" then
proxy_tag = proxy_table.tag or nil proxy_tag = proxy_table.tag or nil
fragment = (proxy_table.fragment and node.protocol ~= "naive" and not node.hysteria2_realms) and true 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 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 end
if node.type ~= "sing-box" then if node.type ~= "sing-box" then
local relay_port = node.port if node.type == "Socks" then
local new_port = api.get_new_port() node.protocol = "socks"
local config_file = string.format("%s_%s_%s.json", flag, tag, new_port) proxy_tag = "socks <- " .. node_id
if tag and node_id and not tag:find(node_id) then else
config_file = string.format("%s_%s_%s_%s.json", flag, tag, node_id, new_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 end
if run_socks_instance then 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', 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", string.format("flag=%s node=%s bind=%s socks_port=%s config_file=%s relay_port=%s",
new_port, --flag new_port, --flag
@@ -119,17 +133,24 @@ function gen_outbound(flag, node, tag, proxy_table)
"127.0.0.1", --bind "127.0.0.1", --bind
new_port, --socks port new_port, --socks port
config_file, --config file config_file, --config file
(proxy_tag and relay_port) and tostring(relay_port) or "" --relay port relay_port --relay port
) )
) )
) )
if relay_port == "" then
api.set_socks_port_to_cache(node_id, new_port)
end end
end
end
if new_port then
node = { node = {
protocol = "socks", protocol = "socks",
address = "127.0.0.1", address = "127.0.0.1",
port = new_port port = new_port
} }
proxy_tag = "socks <- " .. node_id proxy_tag = "socks <- " .. node_id
end
end
else else
if proxy_tag then if proxy_tag then
node.detour = proxy_tag node.detour = proxy_tag
@@ -326,9 +347,20 @@ function gen_outbound(flag, node, tag, proxy_table)
Host = node.ws_host, Host = node.ws_host,
["User-Agent"] = node.user_agent ["User-Agent"] = node.user_agent
} or nil, } or nil,
max_early_data = tonumber(node.ws_maxEarlyData) or nil,
early_data_header_name = (node.ws_earlyDataHeaderName) and node.ws_earlyDataHeaderName or nil --要与 Xray-core 兼容,请将其设置为 Sec-WebSocket-Protocol。它需要与服务器保持一致。
} }
local path = api.UrlDecode(node.ws_path)
local path_dat = api.split(path, "?")
local params = {}
for _, v in pairs(api.split(path_dat[2], '&')) do
local t = api.split(v, '=')
params[t[1]] = t[2]
end
local ed = tonumber(params.ed)
if ed then
v2ray_transport.path = path_dat[1]
v2ray_transport.max_early_data = ed
end
v2ray_transport.early_data_header_name = params.eh or "Sec-WebSocket-Protocol"
end end
if node.transport == "httpupgrade" then if node.transport == "httpupgrade" then
@@ -481,9 +513,9 @@ function gen_outbound(flag, node, tag, proxy_table)
obfs = node.hysteria_obfs, obfs = node.hysteria_obfs,
auth = (node.hysteria_auth_type == "base64") and node.hysteria_auth_password or nil, 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, 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 stream_receive_window = tonumber(node.hysteria_recv_window_conn),
recv_window = tonumber(node.hysteria_recv_window), --1.14 将变更为 connection_receive_window connection_receive_window = tonumber(node.hysteria_recv_window),
disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false, --1.14 将变更为 disable_path_mtu_discovery disable_path_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false,
tls = tls tls = tls
} }
end end
@@ -741,7 +773,7 @@ function gen_config_server(node)
type = "ws", type = "ws",
path = node.ws_path or "/", path = node.ws_path or "/",
headers = (node.ws_host ~= nil) and { Host = node.ws_host } or nil, headers = (node.ws_host ~= nil) and { Host = node.ws_host } or nil,
early_data_header_name = (node.ws_earlyDataHeaderName) and node.ws_earlyDataHeaderName or nil --要与 Xray-core 兼容,请将其设置为 Sec-WebSocket-Protocol。它需要与服务器保持一致。 early_data_header_name = (node.ws_earlyDataHeaderName) and node.ws_earlyDataHeaderName or "Sec-WebSocket-Protocol"
} }
end end
@@ -910,10 +942,10 @@ function gen_config_server(node)
down_mbps = tonumber(node.hysteria_down_mbps), down_mbps = tonumber(node.hysteria_down_mbps),
obfs = node.hysteria_obfs, obfs = node.hysteria_obfs,
users = users, 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 stream_receive_window = node.hysteria_recv_window_conn and tonumber(node.hysteria_recv_window_conn) or nil,
recv_window_client = node.hysteria_recv_window_client and tonumber(node.hysteria_recv_window_client) or nil, --1.14 to connection_receive_window connection_receive_window = node.hysteria_recv_window_client and tonumber(node.hysteria_recv_window_client) or nil,
max_conn_client = node.hysteria_max_conn_client and tonumber(node.hysteria_max_conn_client) or nil, --1.14 to max_concurrent_streams max_concurrent_streams = node.hysteria_max_conn_client and tonumber(node.hysteria_max_conn_client) or nil,
disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false, --1.14 to disable_path_mtu_discover disable_path_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false,
tls = tls tls = tls
} }
end end
@@ -1140,10 +1172,11 @@ function gen_config(var)
local dns_cache = var["dns_cache"] local dns_cache = var["dns_cache"]
local dns_socks_address = var["dns_socks_address"] local dns_socks_address = var["dns_socks_address"]
local dns_socks_port = var["dns_socks_port"] local dns_socks_port = var["dns_socks_port"]
local no_run = var["no_run"]
local use_proxy_list = var["use_proxy_list"] local use_proxy_list = var["use_proxy_list"]
local use_gfw_list = var["use_gfw_list"] local use_gfw_list = var["use_gfw_list"]
local chn_list = var["chn_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_domain_rules = {}
local dns = nil local dns = nil
@@ -1337,7 +1370,9 @@ function gen_config(var)
ut_nodes = _node.urltest_node ut_nodes = _node.urltest_node
end 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 = {} local valid_nodes = {}
for i = 1, #(ut_nodes or {}) do for i = 1, #(ut_nodes or {}) do
@@ -1352,7 +1387,7 @@ function gen_config(var)
end end
end end
if is_new_ut_node then 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 if outboundTag then
valid_nodes[#valid_nodes + 1] = outboundTag valid_nodes[#valid_nodes + 1] = outboundTag
end end
@@ -1455,7 +1490,6 @@ function gen_config(var)
to_node.port = new_port to_node.port = new_port
to_outbound = gen_outbound(node[".name"], to_node, tag, { to_outbound = gen_outbound(node[".name"], to_node, tag, {
tag = tag, tag = tag,
run_socks_instance = not no_run
}) })
else else
to_outbound = gen_outbound(node[".name"], to_node) to_outbound = gen_outbound(node[".name"], to_node)
@@ -1548,7 +1582,6 @@ function gen_config(var)
local proxy_table = { local proxy_table = {
fragment = singbox_settings.fragment == "1", fragment = singbox_settings.fragment == "1",
record_fragment = singbox_settings.record_fragment == "1", record_fragment = singbox_settings.record_fragment == "1",
run_socks_instance = not no_run,
} }
local preproxy_node_id = node[rule_name .. "_proxy_tag"] local preproxy_node_id = node[rule_name .. "_proxy_tag"]
if preproxy_node_id == _node_id then preproxy_node_id = nil end if preproxy_node_id == _node_id then preproxy_node_id = nil end
@@ -1832,7 +1865,6 @@ function gen_config(var)
COMMON.default_outbound_tag = gen_outbound_get_tag(flag, node or node_id, nil, { COMMON.default_outbound_tag = gen_outbound_get_tag(flag, node or node_id, nil, {
fragment = singbox_settings.fragment == "1" or nil, fragment = singbox_settings.fragment == "1" or nil,
record_fragment = singbox_settings.record_fragment == "1" or nil, record_fragment = singbox_settings.record_fragment == "1" or nil,
run_socks_instance = not no_run
}) })
end end
@@ -2216,14 +2248,10 @@ function gen_config(var)
}) })
else else
table.insert(dns.rules, 1, { table.insert(dns.rules, 1, {
action = "evaluate", preferred_by = "hosts",
action = "route",
server = "hosts" server = "hosts"
}) })
table.insert(dns.rules, 2, {
match_response = true,
ip_accept_any = true,
action = "respond"
})
end end
if COMMON.default_outbound_tag == "block" then if COMMON.default_outbound_tag == "block" then
@@ -2297,7 +2325,7 @@ function gen_config(var)
routing_mark = 255, routing_mark = 255,
}) })
for index, value in ipairs(config.outbounds) do 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")) sys.call(string.format("echo '%s' >> %s", value["_id"], api.TMP_PATH .. "/direct_node_list"))
end end
if not value.detour and not value.bind_interface and value.server then if not value.detour and not value.bind_interface and value.server then
@@ -2445,7 +2473,7 @@ if arg[1] then
var = jsonc.parse(arg[2]) var = jsonc.parse(arg[2])
end end
print(func(var)) 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() convert_geofile()
end end
end end
+77 -62
View File
@@ -12,7 +12,7 @@ local GLOBAL = {
local xray_version = api.get_app_version("xray") 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 function get_domain_excluded()
local path = string.format("/usr/share/%s/rules/domains_excluded", api.c_config) local path = string.format("/usr/share/%s/rules/domains_excluded", api.c_config)
@@ -43,29 +43,39 @@ function gen_outbound(flag, node, tag, proxy_table)
end end
local remarks = node.remarks local remarks = node.remarks
local proxy_tag = nil local proxy_tag, dialer_proxy_tag, fragment, noise
local fragment = nil
local noise = nil
local run_socks_instance = true
if proxy_table ~= nil and type(proxy_table) == "table" then if proxy_table ~= nil and type(proxy_table) == "table" then
proxy_tag = proxy_table.tag or nil proxy_tag = proxy_table.tag or nil
fragment = (proxy_table.fragment and not node.hysteria2_realms) and true 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 noise = (proxy_table.noise and not node.hysteria2_realms) and true or nil
run_socks_instance = proxy_table.run_socks_instance
end end
if node.type ~= "Xray" then if node.type ~= "Xray" then
if node.type == "Socks" then if node.type == "Socks" then
node.protocol = "socks" node.protocol = "socks"
node.transport = "tcp" node.transport = "raw"
else else
local relay_port = node.port local new_port
local new_port = api.get_new_port() local run_socks_instance = true
local config_file = string.format("%s_%s_%s.json", flag, tag, new_port) if NO_RUN then
if tag and node_id and not tag:find(node_id) then TMP_PORT = TMP_PORT and TMP_PORT + 1 or 3001
config_file = string.format("%s_%s_%s_%s.json", flag, tag, node_id, new_port) 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 end
if run_socks_instance then 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', 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", string.format("flag=%s node=%s bind=%s socks_port=%s config_file=%s relay_port=%s",
new_port, --flag new_port, --flag
@@ -73,25 +83,27 @@ function gen_outbound(flag, node, tag, proxy_table)
"127.0.0.1", --bind "127.0.0.1", --bind
new_port, --socks port new_port, --socks port
config_file, --config file config_file, --config file
(proxy_tag and relay_port) and tostring(relay_port) or "" --relay port relay_port --relay port
) )
)) )
)
if relay_port == "" then
api.set_socks_port_to_cache(node_id, new_port)
end end
end
end
if new_port then
node = {} node = {}
node.protocol = "socks" node.protocol = "socks"
node.transport = "tcp" node.transport = "raw"
node.address = "127.0.0.1" node.address = "127.0.0.1"
node.port = new_port node.port = new_port
end end
end
node.stream_security = "none" node.stream_security = "none"
proxy_tag = "socks <- " .. node_id proxy_tag = "socks <- " .. node_id
else else
if proxy_tag then dialer_proxy_tag = proxy_tag
node.proxySettings = {
tag = proxy_tag,
transportLayer = true
}
end
end end
if node.type == "Xray" then if node.type == "Xray" then
@@ -124,6 +136,10 @@ function gen_outbound(flag, node, tag, proxy_table)
node.stream_security = "tls" node.stream_security = "tls"
end end
if node.protocol == "http" and node.stream_security == "tls" then
node.transport = "raw"
end
if remarks then if remarks then
tag = tag .. ":" .. remarks tag = tag .. ":" .. remarks
end end
@@ -135,7 +151,6 @@ function gen_outbound(flag, node, tag, proxy_table)
_flag = flag, _flag = flag,
_flag_proxy_tag = proxy_tag, _flag_proxy_tag = proxy_tag,
tag = tag, tag = tag,
proxySettings = node.proxySettings or nil,
protocol = node.protocol, protocol = node.protocol,
mux = { mux = {
enabled = (node.mux == "1") and true or false, 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 xudpConcurrency = (node.mux == "1" and ((node.xudp_concurrency) and tonumber(node.xudp_concurrency) or 8)) or nil
} 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 = { sockopt = {
mark = 255, mark = 255,
domainStrategy = node.domain_strategy or "UseIP", domainStrategy = node.domain_strategy or "UseIP",
@@ -154,9 +169,10 @@ function gen_outbound(flag, node, tag, proxy_table)
PrioritizeIPv6 = false, PrioritizeIPv6 = false,
Interleave = 1, Interleave = 1,
MaxConcurrentTry = 4 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, security = node.stream_security,
tlsSettings = (node.stream_security == "tls") and { tlsSettings = (node.stream_security == "tls") and {
serverName = node.tls_serverName, serverName = node.tls_serverName,
@@ -548,7 +564,7 @@ function gen_config_server(node)
allowTransparent = false, allowTransparent = false,
users = users users = users
} }
node.transport = "tcp" node.transport = "raw"
node.tcp_guise = "none" node.tcp_guise = "none"
elseif node.protocol == "shadowsocks" then elseif node.protocol == "shadowsocks" then
settings = { settings = {
@@ -675,7 +691,7 @@ function gen_config_server(node)
protocol = node.protocol, protocol = node.protocol,
settings = settings, settings = settings,
streamSettings = { 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", security = "none",
tlsSettings = ("1" == node.tls) and { tlsSettings = ("1" == node.tls) and {
disableSystemRoot = false, disableSystemRoot = false,
@@ -857,9 +873,6 @@ function gen_config_server(node)
config.outbounds[index][k] = nil config.outbounds[index][k] = nil
end end
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 end
return config return config
@@ -897,10 +910,11 @@ function gen_config(var)
local dns_socks_address = var["dns_socks_address"] local dns_socks_address = var["dns_socks_address"]
local dns_socks_port = var["dns_socks_port"] local dns_socks_port = var["dns_socks_port"]
local loglevel = var["loglevel"] or "warning" local loglevel = var["loglevel"] or "warning"
local no_run = var["no_run"]
local use_proxy_list = var["use_proxy_list"] local use_proxy_list = var["use_proxy_list"]
local use_gfw_list = var["use_gfw_list"] local use_gfw_list = var["use_gfw_list"]
local chn_list = var["chn_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_domain_rules = {}
local dns = nil local dns = nil
@@ -1075,7 +1089,9 @@ function gen_config(var)
blc_nodes = _node.balancing_node blc_nodes = _node.balancing_node
end 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 = {} local valid_nodes = {}
for i = 1, #(blc_nodes or {}) do for i = 1, #(blc_nodes or {}) do
@@ -1090,7 +1106,7 @@ function gen_config(var)
end end
end end
if is_new_blc_node then 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 if outboundTag then
valid_nodes[#valid_nodes + 1] = outboundTag valid_nodes[#valid_nodes + 1] = outboundTag
end end
@@ -1119,7 +1135,7 @@ function gen_config(var)
local fallback_node = get_node_by_id(fallback_node_id) local fallback_node = get_node_by_id(fallback_node_id)
if fallback_node then if fallback_node then
if fallback_node.protocol ~= "_balancing" 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 if outboundTag then
fallback_node_tag = outboundTag fallback_node_tag = outboundTag
end end
@@ -1209,10 +1225,9 @@ function gen_config(var)
end end
if preproxy_outbound then if preproxy_outbound then
outbound.tag = preproxy_outbound.tag .. " -> " .. outbound.tag outbound.tag = preproxy_outbound.tag .. " -> " .. outbound.tag
outbound.proxySettings = { outbound.streamSettings = outbound.streamSettings or {}
tag = preproxy_outbound.tag, outbound.streamSettings.sockopt = outbound.streamSettings.sockopt or {}
transportLayer = true outbound.streamSettings.sockopt.dialerProxy = preproxy_outbound.tag
}
if not exist then if not exist then
last_insert_outbound = preproxy_outbound last_insert_outbound = preproxy_outbound
end end
@@ -1252,7 +1267,6 @@ function gen_config(var)
}) })
to_outbound = gen_outbound(node[".name"], to_node, to_node[".name"], { to_outbound = gen_outbound(node[".name"], to_node, to_node[".name"], {
tag = to_node[".name"], tag = to_node[".name"],
run_socks_instance = not no_run
}) })
else else
to_outbound = gen_outbound(node[".name"], to_node) to_outbound = gen_outbound(node[".name"], to_node)
@@ -1260,10 +1274,9 @@ function gen_config(var)
if to_outbound then if to_outbound then
to_outbound.tag = outbound.tag .. " -> " .. to_outbound.tag to_outbound.tag = outbound.tag .. " -> " .. to_outbound.tag
if to_node.type == "Xray" then if to_node.type == "Xray" then
to_outbound.proxySettings = { to_outbound.streamSettings = to_outbound.streamSettings or {}
tag = outbound.tag, to_outbound.streamSettings.sockopt = to_outbound.streamSettings.sockopt or {}
transportLayer = true to_outbound.streamSettings.sockopt.dialerProxy = outbound.tag
}
end end
table.insert(outbounds_table, to_outbound) table.insert(outbounds_table, to_outbound)
default_outTag = to_outbound.tag default_outTag = to_outbound.tag
@@ -1318,9 +1331,9 @@ function gen_config(var)
interface = node.iface interface = node.iface
} }
}, },
settings = (api.compare_versions(xray_version, ">", "26.4.25")) and { -- Todo: Remove version check settings = {
finalRules = {{ action = "allow" }} 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)) sys.call(string.format("mkdir -p %s && touch %s/%s", api.TMP_IFACE_PATH, api.TMP_IFACE_PATH, node.iface))
end end
@@ -1361,7 +1374,6 @@ function gen_config(var)
local proxy_table = { local proxy_table = {
fragment = xray_settings.fragment == "1", fragment = xray_settings.fragment == "1",
noise = xray_settings.noise == "1", noise = xray_settings.noise == "1",
run_socks_instance = not no_run,
} }
local preproxy_node_id = node[rule_name .. "_proxy_tag"] local preproxy_node_id = node[rule_name .. "_proxy_tag"]
if preproxy_node_id == _node_id then preproxy_node_id = nil end 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, { COMMON.default_outbound_tag = gen_outbound_get_tag(flag, node or node_id, nil, {
fragment = xray_settings.fragment == "1" or nil, fragment = xray_settings.fragment == "1" or nil,
noise = xray_settings.noise == "1" or nil, noise = xray_settings.noise == "1" or nil,
run_socks_instance = not no_run
}) })
if COMMON.default_outbound_tag then if COMMON.default_outbound_tag then
routing = { routing = {
@@ -1800,15 +1811,14 @@ function gen_config(var)
remote_dns_outbound = { remote_dns_outbound = {
tag = "dns-out", tag = "dns-out",
protocol = "dns", protocol = "dns",
proxySettings = dns_outbound_tag and { streamSettings = dns_outbound_tag and {
tag = (dns_outbound_tag ~= "blackhole") and dns_outbound_tag or "direct" sockopt = { dialerProxy = (dns_outbound_tag ~= "blackhole") and dns_outbound_tag or "direct" }
} or nil, } or nil,
settings = { settings = {
address = (chn_list ~= "proxy") and "8.8.8.8" or "223.5.5.5", rewriteAddress = (chn_list ~= "proxy") and "8.8.8.8" or "223.5.5.5",
port = 53, rewritePort = 53,
network = "tcp", rewriteNetwork = "tcp",
nonIPQuery = (api.compare_versions(xray_version, "<", "26.4.25")) and "reject" or nil, -- Todo is to remove it rules = {}
rules = (api.compare_versions(xray_version, ">", "26.4.17")) and {} or nil
} }
} }
@@ -2033,12 +2043,12 @@ function gen_config(var)
protocol = "freedom", protocol = "freedom",
tag = "direct", tag = "direct",
settings = { settings = {
domainStrategy = (direct_dns_query_strategy and direct_dns_query_strategy ~= "") and direct_dns_query_strategy or "UseIP", finalRules = {{ action = "allow" }}
finalRules = (api.compare_versions(xray_version, ">", "26.4.25")) and {{ action = "allow" }} or nil -- Todo: Remove version check
}, },
streamSettings = { streamSettings = {
sockopt = { 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 for index, value in ipairs(config.outbounds) do
local pt = value.protocol local pt = value.protocol
local exclude = { blackhole=1, dns=1, freedom=1, loopback=1 } 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")) sys.call(string.format("echo '%s' >> %s", value["_id"], api.TMP_PATH .. "/direct_node_list"))
end end
for k, v in pairs(config.outbounds[index]) do for k, v in pairs(config.outbounds[index]) do
@@ -2138,7 +2148,7 @@ function gen_proto_config(var)
local outbound = { local outbound = {
protocol = server_proto, protocol = server_proto,
streamSettings = { streamSettings = {
network = "tcp", method = "raw",
security = "none" security = "none"
}, },
settings = { settings = {
@@ -2163,10 +2173,12 @@ function gen_proto_config(var)
table.insert(outbounds, { table.insert(outbounds, {
protocol = "freedom", protocol = "freedom",
tag = "direct", tag = "direct",
settings = (api.compare_versions(xray_version, ">", "26.4.25")) and { -- Todo: Remove version check settings = {
finalRules = {{ action = "allow" }} finalRules = {{ action = "allow" }}
} or nil, },
streamSettings = {
sockopt = {mark = 255} sockopt = {mark = 255}
}
}) })
local config = { local config = {
@@ -2178,7 +2190,10 @@ function gen_proto_config(var)
-- 传出连接 -- 传出连接
outbounds = outbounds, outbounds = outbounds,
-- 路由 -- 路由
routing = routing routing = routing,
version = {
min = xray_min_version
}
} }
return jsonc.stringify(config, 1) return jsonc.stringify(config, 1)
end 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> </script>
<div id="<%=cbid%>.main" class="dl-dropdown-container"> <div id="<%=cbid%>.main" class="dl-dropdown-container">
<div style="display:none !important;">
<select id="<%=cbid%>.ref" class="cbi-input-select" style="display:none !important;"> <select id="<%=cbid%>.ref" class="cbi-input-select" style="display:none !important;">
<option value>placeholder</option> <option value>placeholder</option>
</select> </select>
</div>
<!-- 已选节点显示区 --> <!-- 已选节点显示区 -->
<div id="<%=cbid%>.selected" class="dl-selected-list"></div> <div id="<%=cbid%>.selected" class="dl-selected-list"></div>
<!-- 模拟 listvalue 控件外观 --> <!-- 模拟 listvalue 控件外观 -->
<div class="cbi-input-value cbi-input-select dl-dropdown-display" id="<%=cbid%>.display" tabindex="0"> <div class="cbi-input-value cbi-input-select dl-dropdown-display" id="<%=cbid%>.display" tabindex="0">
<span id="<%=cbid%>.label" class="dl-dropdown-label"> <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"> <div id="<%=cbid%>.main" class="lv-dropdown-container">
<!-- 隐藏 select(保存实际配置值) --> <!-- 隐藏 select(保存实际配置值) -->
<div style="display:none !important;">
<select id="<%=cbid%>" name="<%=cbid%>" class="cbi-input-select" data-update="change" 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> <option value="<%=current_key%>" selected="selected">placeholder</option>
</select> </select>
</div>
<!-- 模拟 ListValue 控件外观 --> <!-- 模拟 ListValue 控件外观 -->
<div class="cbi-input-value cbi-input-select lv-dropdown-display" id="<%=cbid%>.display" tabindex="0"> <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)%>"> <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;"> <div id="<%=cbid%>" class="cbi-input-select" style="display:inline-block;">
<div style="display:none !important;">
<select id="<%=cbid%>.ref" class="cbi-input-select" style="display:none !important;"> <select id="<%=cbid%>.ref" class="cbi-input-select" style="display:none !important;">
<option value>placeholder</option> <option value>placeholder</option>
</select> </select>
</div>
<!-- 搜索框 --> <!-- 搜索框 -->
<input type="text" id="<%=cbid%>.search" <input type="text" id="<%=cbid%>.search"
class="mv_search_input cbi-input-text" class="mv_search_input cbi-input-text"
@@ -97,9 +97,11 @@ window.v_dropdown_data["<%=cbid%>"] = <%=json.stringify(dropdown_data)%>;
</script> </script>
<div id="<%=cbid%>.v-main" class="v-dropdown-container"> <div id="<%=cbid%>.v-main" class="v-dropdown-container">
<div style="display:none !important;">
<select id="<%=cbid%>.ref" class="cbi-input-select" style="display:none !important;"> <select id="<%=cbid%>.ref" class="cbi-input-select" style="display:none !important;">
<option value>placeholder</option> <option value>placeholder</option>
</select> </select>
</div>
<!-- 隐藏 input(保存实际配置值) --> <!-- 隐藏 input(保存实际配置值) -->
<input type="text" id="<%=cbid%>" name="<%=cbid%>" value="<%=current_key%>" data-update="change" style="display:none !important;" /> <input type="text" id="<%=cbid%>" name="<%=cbid%>" value="<%=current_key%>" data-update="change" style="display:none !important;" />
<!-- 模拟 ListValue 控件外观 --> <!-- 模拟 ListValue 控件外观 -->
@@ -14,11 +14,6 @@ local sid = "@global[0]"
} }
} }
.shunt-tab-hidden {
display: none !important;
visibility: hidden !important;
}
@media screen and (max-width: 1152px) { @media screen and (max-width: 1152px) {
#cbi-<%=appname%>-socks .cbi-section-table-titles { #cbi-<%=appname%>-socks .cbi-section-table-titles {
display: none !important; display: none !important;
@@ -28,7 +23,6 @@ local sid = "@global[0]"
<script type="text/javascript"> <script type="text/javascript">
//<![CDATA[ //<![CDATA[
let global_id = "<%=sid%>"; let global_id = "<%=sid%>";
const shunt_list = JSON.parse('<%=self.shunt_list%>');
function to_edit_node(btn, stype) { function to_edit_node(btn, stype) {
if (!btn) return; if (!btn) return;
@@ -235,47 +229,5 @@ local sid = "@global[0]"
select.insertAdjacentElement("afterend", logLink); 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> </script>
@@ -1,4 +1,5 @@
<div class="cbi-value" id="cbi-<%=self.config.."-"..section.."-"..self.option%>" data-index="<%=self.index%>" data-depends="<%=pcdata(self:deplist2json(section))%>"> <%+cbi/valueheader%>
<label class="cbi-value-title"> <label class="cbi-value-title">
<%:Switch Mode%> <%:Switch Mode%>
</label> </label>
@@ -10,7 +11,6 @@
<input class="btn cbi-button cbi-button-apply" type="button" onclick="switch_global_mode()" value="<%:Global Proxy%>" /> <input class="btn cbi-button cbi-button-apply" type="button" onclick="switch_global_mode()" value="<%:Global Proxy%>" />
</div> </div>
</div> </div>
</div>
<script> <script>
var opt = { var opt = {
@@ -108,3 +108,4 @@
opt.set("udp_proxy_mode", "proxy"); opt.set("udp_proxy_mode", "proxy");
} }
</script> </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 map = self.map
local api = map.api local api = map.api
local config = map.config local config = map.config
local id = self.id local node_id = self.node_id
local s_cfgid = self.s_cfgid 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> <style>
.hidden { .shunt_hidden {
display: none !important; display: none !important;
visibility: hidden !important; visibility: hidden !important;
} }
</style> </style>
<script type="text/javascript"> <script type="text/javascript">
//<![CDATA[ //<![CDATA[
const normal_list = JSON.parse('<%=self.normal_list%>'); var normal_list = JSON.parse('<%=self.normal_list%>');
function check_fakedns(id, val) { function check_fakedns(id, val) {
const dom_id = "cbid.table." + id + "."; const dom_id = "cbid.table." + id + ".";
const fakedns_dom = document.getElementById(dom_id + "_fakedns"); 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"); const selectNode = document.getElementById(dom_id + "_node");
val = selectNode.value; val = selectNode.value;
} }
if (fakedns_dom) {
if (val == "" || val == "_direct") { if (val == "" || val == "_direct") {
fakedns_dom.value = 0; fakedns_dom.value = 0;
fakedns_dom.checked = false; fakedns_dom.checked = false;
fakedns_dom.classList.add('hidden'); fakedns_dom.classList.add('shunt_hidden');
} else { } else {
fakedns_dom.classList.remove('hidden'); fakedns_dom.classList.remove('shunt_hidden');
}
} }
} }
function check_proxy_tag(id, val) { function check_proxy_tag(id, val) {
@@ -45,13 +51,13 @@ local s_cfgid = self.s_cfgid
const searchInput = document.getElementById(cbid + ".search"); const searchInput = document.getElementById(cbid + ".search");
const listContainer = document.getElementById(cbid + ".list"); const listContainer = document.getElementById(cbid + ".list");
lv_change(cbid, listContainer, hiddenSelect, labelSpan, searchInput, "", "<%:Close (Not use)%>"); lv_change(cbid, listContainer, hiddenSelect, labelSpan, searchInput, "", "<%:Close (Not use)%>");
proxy_tag_dom.classList.add('hidden'); proxy_tag_dom?.classList.add('shunt_hidden');
} else { } else {
proxy_tag_dom.classList.remove('hidden'); proxy_tag_dom?.classList.remove('shunt_hidden');
} }
} }
function refresh_depends() { 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) { if (table_dom) {
const trs = table_dom.getElementsByTagName("tr"); const trs = table_dom.getElementsByTagName("tr");
for (let i = 1; i < trs.length;i++) { for (let i = 1; i < trs.length;i++) {
@@ -60,25 +66,66 @@ 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 () { document.addEventListener("DOMContentLoaded", function () {
shunt.tab();
waitForElement('select[name*="<%=config%>"][name*="shunt_group"]', function(el) { waitForElement('select[name*="<%=config%>"][name*="shunt_group"]', function(el) {
let o_val = el.value; let o_val = el.value;
el.addEventListener("change", () => { el.addEventListener("change", () => {
el.blur(); el.blur();
if (o_val != el.value) { if (o_val != el.value) {
let save = true; update_config("<%=node_id%>", {
if (save) { shunt_group: getOption("<%=config%>", "<%=section_id%>", "shunt_group").value,
update_config("<%=id%>", {
shunt_group: getOption("<%=config%>", "<%=s_cfgid%>", "shunt_group").value,
}, get_current_url()); }, get_current_url());
} }
}
}); });
}); });
}); setTimeout(() => {
document.addEventListener("DOMContentLoaded", () => setTimeout(() => {
refresh_depends(); refresh_depends();
const table_dom = document.getElementById("cbi-passwall-shunt_option_list"); const table_dom = document.getElementById("cbi-<%=config%>-<%=section_type%>");
if (table_dom) { if (table_dom) {
const trs = table_dom.getElementsByTagName("tr"); const trs = table_dom.getElementsByTagName("tr");
for (let i = 1; i < trs.length;i++) { for (let i = 1; i < trs.length;i++) {
@@ -94,6 +141,7 @@ local s_cfgid = self.s_cfgid
} }
} }
} }
}, 100)); }, 200)
});
//]]> //]]>
</script> </script>
@@ -26,36 +26,45 @@ local config = map.config
update_config(node_id, { update_config(node_id, {
remarks: getOption("<%=config%>", "<%=sid%>", "remarks").value, remarks: getOption("<%=config%>", "<%=sid%>", "remarks").value,
group: getOption("<%=config%>", "<%=sid%>", "group").value, group: getOption("<%=config%>", "<%=sid%>", "group").value,
type: getOption("<%=config%>", "<%=sid%>", "type").value, type: el.value,
//protocol: '' //protocol: ''
}, node_config_url); }, 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) { waitForElement('select[name*="<%=config%>"][name*="protocol"]', function(el) {
let o_val = el.value; let o_val = el.value;
const name_split = el.name.split(".");
const name = name_split[name_split.length - 1];
el.addEventListener("change", () => { el.addEventListener("change", () => {
el.blur(); el.blur();
if (el.value.startsWith("_") || (o_val.startsWith("_") && !el.value.startsWith("_"))) { if (el.value.startsWith("_") || (o_val.startsWith("_") && !el.value.startsWith("_"))) {
let name_split = el.name.split("."); if (el.value.startsWith("_")) {
let name = name_split[name_split.length - 1]; const map = document.getElementById("cbi-<%=config%>");
let save = true; const hiddenInput = document.createElement('input');
if (save) { hiddenInput.type = 'hidden';
/* hiddenInput.name = 'load' + el.value;
hiddenInput.value = '1';
map.appendChild(hiddenInput);
}
const saveBtn = document.getElementsByClassName('cbi-button-save'); const saveBtn = document.getElementsByClassName('cbi-button-save');
if (saveBtn && saveBtn.length > 0) { if (saveBtn && saveBtn.length > 0) {
saveBtn[saveBtn.length - 1].click(); saveBtn[saveBtn.length - 1].click();
} }
*/ /*
update_config(node_id, { update_config(node_id, {
remarks: getOption("<%=config%>", "<%=sid%>", "remarks").value, remarks: getOption("<%=config%>", "<%=sid%>", "remarks").value,
group: getOption("<%=config%>", "<%=sid%>", "group").value, group: getOption("<%=config%>", "<%=sid%>", "group").value,
protocol: getOption("<%=config%>", "<%=sid%>", name).value, protocol: el.value,
}, node_config_url); }, node_config_url);
} else { */
window.location.href = node_config_url + "?select_proto=" + encodeURIComponent(el.value);
}
} }
}); });
}); });
@@ -1,8 +1,8 @@
<%+cbi/valueheader%> <%+cbi/valueheader%>
<% <%
local api = require "luci.passwall.api"
local appname = api.appname
local map = self.map local map = self.map
local api = map.api
local config = map.config
local has_ss_rust = api.is_finded("sslocal") local has_ss_rust = api.is_finded("sslocal")
local has_singbox = api.finded_com("sing-box") 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) 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[ <script type="text/javascript">//<![CDATA[
let has_singbox = "<%=has_singbox%>" let has_singbox = "<%=has_singbox%>"
let has_xray = "<%=has_xray%>" let has_xray = "<%=has_xray%>"
@@ -112,8 +112,7 @@ local current_node = map:get(section)
function buildUrl(btn, urlname, sid) { function buildUrl(btn, urlname, sid) {
var opt = { var opt = {
base: "cbid.<%=appname%>", base: "cbid.<%=config%>",
client: true,
get: function (opt) { get: function (opt) {
var id = this.base + "." + opt; var id = this.base + "." + opt;
var obj = document.getElementsByName(id)[0] || document.getElementsByClassName(id)[0] || document.getElementById(id) 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.base = "cbid." + urlname + "." + sid;
opt.client = urlname.indexOf("server") === -1;
var v_type = opt.get("type").value; var v_type = opt.get("type").value;
var v_alias = opt.get("remarks"); var v_alias = opt.get("remarks");
@@ -251,10 +249,6 @@ local current_node = map:get(section)
if (v_transport === "ws") { if (v_transport === "ws") {
params += opt.query("host", dom_prefix + "ws_host"); params += opt.query("host", dom_prefix + "ws_host");
params += opt.query("path", dom_prefix + "ws_path"); params += opt.query("path", dom_prefix + "ws_path");
if (v_type == "sing-box" && opt.get(dom_prefix + "ws_enableEarlyData").checked) {
var ws_maxEarlyData = opt.get(dom_prefix + "ws_maxEarlyData").value;
params += "?ed=" + ws_maxEarlyData;
}
} else if (v_transport === "http") { } else if (v_transport === "http") {
params += opt.query("host", dom_prefix + "http_host"); params += opt.query("host", dom_prefix + "http_host");
params += opt.query("path", dom_prefix + "http_path"); params += opt.query("path", dom_prefix + "http_path");
@@ -373,10 +367,6 @@ local current_node = map:get(section)
if (v_transport === "ws") { if (v_transport === "ws") {
info.host = opt.get(dom_prefix + "ws_host").value; info.host = opt.get(dom_prefix + "ws_host").value;
info.path = opt.get(dom_prefix + "ws_path").value; info.path = opt.get(dom_prefix + "ws_path").value;
if (v_type == "sing-box" && opt.get(dom_prefix + "ws_enableEarlyData").checked) {
var ws_maxEarlyData = opt.get(dom_prefix + "ws_maxEarlyData").value;
info.path = info.path + "?ed=" + ws_maxEarlyData;
}
} else if (v_transport === "http") { } else if (v_transport === "http") {
info.host = opt.get(dom_prefix + "http_host").value; info.host = opt.get(dom_prefix + "http_host").value;
info.path = opt.get(dom_prefix + "http_path").value; info.path = opt.get(dom_prefix + "http_path").value;
@@ -433,10 +423,6 @@ local current_node = map:get(section)
if (v_transport === "ws") { if (v_transport === "ws") {
params += opt.query("host", dom_prefix + "ws_host"); params += opt.query("host", dom_prefix + "ws_host");
params += opt.query("path", dom_prefix + "ws_path"); params += opt.query("path", dom_prefix + "ws_path");
if (v_type == "sing-box" && opt.get(dom_prefix + "ws_enableEarlyData").checked) {
var ws_maxEarlyData = opt.get(dom_prefix + "ws_maxEarlyData").value;
params += encodeURIComponent("?ed=" + ws_maxEarlyData);
}
} else if (v_transport === "http") { } else if (v_transport === "http") {
params += opt.query("host", dom_prefix + "http_host"); params += opt.query("host", dom_prefix + "http_host");
params += opt.query("path", dom_prefix + "http_path"); params += opt.query("path", dom_prefix + "http_path");
@@ -525,10 +511,6 @@ local current_node = map:get(section)
if (v_transport === "ws") { if (v_transport === "ws") {
params += opt.query("host", dom_prefix + "ws_host"); params += opt.query("host", dom_prefix + "ws_host");
params += opt.query("path", dom_prefix + "ws_path"); params += opt.query("path", dom_prefix + "ws_path");
if (v_type == "sing-box" && opt.get(dom_prefix + "ws_enableEarlyData").checked) {
var ws_maxEarlyData = opt.get(dom_prefix + "ws_maxEarlyData").value;
params += "?ed=" + ws_maxEarlyData;
}
} else if (v_transport === "http") { } else if (v_transport === "http") {
params += opt.query("host", dom_prefix + "http_host"); params += opt.query("host", dom_prefix + "http_host");
params += opt.query("path", dom_prefix + "http_path"); params += opt.query("path", dom_prefix + "http_path");
@@ -753,34 +735,41 @@ local current_node = map:get(section)
return server.replace(/^\[/, '').replace(/\]$/, ''); return server.replace(/^\[/, '').replace(/\]$/, '');
} }
function fromUrl(btn, urlname, sid, cacheData) { function tofromUrl(btn) {
var opt = { let link = prompt('<%:Paste Share URL Here%>', '');
base: 'cbid.<%=appname%>', if (link === null || link === "") {
client: true, 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) { get: function (opt) {
var obj; return getOption("<%=config%>", "<%=self.value%>", opt);
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;
}
}, },
set: function (opt, val) { set: function (opt, val) {
if (!switch_type && opt == "type" && current_type != val) {
switch_type = val;
return;
}
var obj; var obj;
obj = this.get(opt); obj = this.get(opt);
if (obj) { if (obj) {
var event = document.createEvent("HTMLEvents");
event.initEvent("change", true, true);
if (obj.type === 'checkbox') { if (obj.type === 'checkbox') {
obj.checked = val; obj.checked = val;
} else { } else {
@@ -814,17 +803,17 @@ local current_node = map:get(section)
} }
} }
try { try {
obj.dispatchEvent(event); obj.dispatchEvent(new Event("change", { bubbles: true, cancelable: true }));
} catch (err) {} } catch (err) {}
} else { } else {
//alert('<%:Faltal on set option, please help in debug: %>' + opt + ' = ' + val); //alert('<%:Faltal on set option, please help in debug: %>' + opt + ' = ' + val);
// 处理 DynamicList // Processing DynamicList
var fullName = this.base + '.' + opt; var fullName = this.base + '.' + opt;
var lists = document.querySelectorAll('.cbi-dynlist'); var lists = document.querySelectorAll('.cbi-dynlist');
for (var i = 0; i < lists.length; i++) { for (var i = 0; i < lists.length; i++) {
var parent = lists[i].closest('.cbi-value'); var parent = lists[i].closest('.cbi-value');
if (!parent) continue; 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 label = parent.querySelector('label.cbi-value-title');
var labelFor = label?.getAttribute('for'); var labelFor = label?.getAttribute('for');
if (labelFor === fullName) { if (labelFor === fullName) {
@@ -838,57 +827,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'); link = link.split(/\r\n|\r|\n/, 1)[0];
if (!s) { link = link.replace(/&amp;/gi, '&').replace(/\s*#\s*/, '#').trim(); //一些奇葩的链接用"&amp;"当做"&""#"前后带空格
alert("Never"); const link_split = link.split('://');
return false; const link_type = link_split[0];
}
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);
var dom_prefix = null let dom_prefix = null;
if (ssu[0] === "ssr") { if (link_type === "ssr") {
dom_prefix = "ssr_" dom_prefix = "ssr_"
//var b64c = ssu[1].match(/([A-Za-z0-9_-]+)/); //var b64c = link_split[1].match(/([A-Za-z0-9_-]+)/);
var sstr = b64decsafe((ssu[1] || "").replace(/#.*/, "").trim()); var sstr = b64decsafe((link_split[1] || "").replace(/#.*/, "").trim());
var ploc = sstr.indexOf("/?"); var ploc = sstr.indexOf("/?");
var url0 = "", param = ""; var url0 = "", param = "";
if (ploc > 0) { if (ploc > 0) {
@@ -923,8 +875,8 @@ local current_node = map:get(section)
if (typeof (rem) !== 'undefined' && rem !== '' && rem.length > 0) if (typeof (rem) !== 'undefined' && rem !== '' && rem.length > 0)
opt.set('remarks', b64decutf8safe(rem)); opt.set('remarks', b64decutf8safe(rem));
} }
if (ssu[0] === "ss") { if (link_type === "ss") {
var url0 = ssu[1] || ""; var url0 = link_split[1] || "";
param = ""; param = "";
var ploc = url0.indexOf("#"); var ploc = url0.indexOf("#");
@@ -999,15 +951,15 @@ local current_node = map:get(section)
queryParam.type && queryParam.type !== "tcp" && queryParam.type && queryParam.type !== "tcp" &&
queryParam.headerType && queryParam.headerType !== "none"; queryParam.headerType && queryParam.headerType !== "none";
if (has_xray && (ss_type == "xray" || needUpgrade || queryParam.type === "xhttp")) { if (has_xray && (ss_type == "xray" || needUpgrade || queryParam.type === "xhttp")) {
dom_prefix = "xray_" dom_prefix = "xray_";
opt.set('type', "Xray"); opt.set('type', "Xray");
opt.set(dom_prefix + 'protocol', "shadowsocks"); opt.set(dom_prefix + 'protocol', "shadowsocks");
} else if (has_singbox && (ss_type == "sing-box" || needUpgrade)) { } else if (has_singbox && (ss_type == "sing-box" || needUpgrade)) {
dom_prefix = "singbox_" dom_prefix = "singbox_";
opt.set('type', "sing-box"); opt.set('type', "sing-box");
opt.set(dom_prefix + 'protocol', "shadowsocks"); opt.set(dom_prefix + 'protocol', "shadowsocks");
} else { } else {
dom_prefix = "ssrust_" dom_prefix = "ssrust_";
opt.set('type', "SS-Rust"); opt.set('type', "SS-Rust");
} }
@@ -1117,24 +1069,6 @@ local current_node = map:get(section)
} else if (queryParam.type === "ws") { } else if (queryParam.type === "ws") {
opt.set(dom_prefix + 'ws_host', queryParam.host || ""); opt.set(dom_prefix + 'ws_host', queryParam.host || "");
opt.set(dom_prefix + 'ws_path', queryParam.path || ""); opt.set(dom_prefix + 'ws_path', queryParam.path || "");
if (dom_prefix == "singbox_" && queryParam.path && queryParam.path.length > 1) {
var ws_path_params = {};
var ws_path_dat = queryParam.path.split('?');
var ws_path = ws_path_dat[0];
var ws_path_params = {};
var ws_path_params_array = (ws_path_dat[1] || '').split('&');
for (i = 0; i < ws_path_params_array.length; i++) {
var kv = ws_path_params_array[i].split('=');
ws_path_params[decodeURIComponent(kv[0]).toLowerCase()] = decodeURIComponent(kv[1] || '');
}
if (ws_path_params.ed) {
opt.set(dom_prefix + 'ws_path', ws_path);
opt.set(dom_prefix + 'ws_enableEarlyData', true);
opt.set(dom_prefix + 'ws_maxEarlyData', ws_path_params.ed);
opt.set(dom_prefix + 'ws_earlyDataHeaderName', 'Sec-WebSocket-Protocol');
}
}
} else if (queryParam.type === "http") { } else if (queryParam.type === "http") {
if (dom_prefix == "xray_") { if (dom_prefix == "xray_") {
opt.set(dom_prefix + 'xhttp_mode', "stream-one"); opt.set(dom_prefix + 'xhttp_mode', "stream-one");
@@ -1207,17 +1141,17 @@ local current_node = map:get(section)
} }
} }
} }
if (ssu[0] === "trojan") { if (link_type === "trojan") {
if (trojan_type == "sing-box" && has_singbox) { if (trojan_type == "sing-box" && has_singbox) {
dom_prefix = "singbox_" dom_prefix = "singbox_";
opt.set('type', "sing-box"); opt.set('type', "sing-box");
opt.set(dom_prefix + 'protocol', "trojan"); opt.set(dom_prefix + 'protocol', "trojan");
} else if (trojan_type == "xray" && has_xray) { } else if (trojan_type == "xray" && has_xray) {
dom_prefix = "xray_" dom_prefix = "xray_";
opt.set('type', "Xray"); opt.set('type', "Xray");
opt.set(dom_prefix + 'protocol', "trojan"); opt.set(dom_prefix + 'protocol', "trojan");
} }
var m = parseNodeUrl(ssrurl); var m = parseNodeUrl(link);
var password = m.passwd; var password = m.passwd;
if (password === "") { if (password === "") {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>"; s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
@@ -1264,24 +1198,6 @@ local current_node = map:get(section)
} else if (queryParam.type === "ws") { } else if (queryParam.type === "ws") {
opt.set(dom_prefix + 'ws_host', queryParam.host || ""); opt.set(dom_prefix + 'ws_host', queryParam.host || "");
opt.set(dom_prefix + 'ws_path', queryParam.path || ""); opt.set(dom_prefix + 'ws_path', queryParam.path || "");
if (dom_prefix == "singbox_" && queryParam.path && queryParam.path.length > 1) {
var ws_path_params = {};
var ws_path_dat = queryParam.path.split('?');
var ws_path = ws_path_dat[0];
var ws_path_params = {};
var ws_path_params_array = (ws_path_dat[1] || '').split('&');
for (i = 0; i < ws_path_params_array.length; i++) {
var kv = ws_path_params_array[i].split('=');
ws_path_params[decodeURIComponent(kv[0]).toLowerCase()] = decodeURIComponent(kv[1] || '');
}
if (ws_path_params.ed) {
opt.set(dom_prefix + 'ws_path', ws_path);
opt.set(dom_prefix + 'ws_enableEarlyData', true);
opt.set(dom_prefix + 'ws_maxEarlyData', ws_path_params.ed);
opt.set(dom_prefix + 'ws_earlyDataHeaderName', 'Sec-WebSocket-Protocol');
}
}
} else if (queryParam.type === "http") { } else if (queryParam.type === "http") {
if (dom_prefix == "xray_") { if (dom_prefix == "xray_") {
opt.set(dom_prefix + 'xhttp_mode', "stream-one"); opt.set(dom_prefix + 'xhttp_mode', "stream-one");
@@ -1345,14 +1261,14 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(m.hash.substr(1))); opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
} }
} }
if (ssu[0] === "vmess") { if (link_type === "vmess") {
var sstr = b64DecodeUnicode((ssu[1] || "").replace(/#.*/, "").trim()); var sstr = b64DecodeUnicode((link_split[1] || "").replace(/#.*/, "").trim());
var ploc = sstr.indexOf("/?"); var ploc = sstr.indexOf("/?");
if (vmess_type == "sing-box" && has_singbox) { if (vmess_type == "sing-box" && has_singbox) {
dom_prefix = "singbox_" dom_prefix = "singbox_";
opt.set('type', "sing-box"); opt.set('type', "sing-box");
} else if (has_xray) { } else if (has_xray) {
dom_prefix = "xray_" dom_prefix = "xray_";
opt.set('type', "Xray"); opt.set('type', "Xray");
} }
opt.set(dom_prefix + 'protocol', "vmess"); opt.set(dom_prefix + 'protocol', "vmess");
@@ -1406,24 +1322,6 @@ local current_node = map:get(section)
} else if (ssm.net === "ws") { } else if (ssm.net === "ws") {
opt.set(dom_prefix + 'ws_host', ssm.host); opt.set(dom_prefix + 'ws_host', ssm.host);
opt.set(dom_prefix + 'ws_path', ssm.path); opt.set(dom_prefix + 'ws_path', ssm.path);
if (dom_prefix == "singbox_" && ssm.path && ssm.path.length > 1) {
var ws_path_params = {};
var ws_path_dat = ssm.path.split('?');
var ws_path = ws_path_dat[0];
var ws_path_params = {};
var ws_path_params_array = (ws_path_dat[1] || '').split('&');
for (i = 0; i < ws_path_params_array.length; i++) {
var kv = ws_path_params_array[i].split('=');
ws_path_params[decodeURIComponent(kv[0]).toLowerCase()] = decodeURIComponent(kv[1] || '');
}
if (ws_path_params.ed) {
opt.set(dom_prefix + 'ws_path', ws_path);
opt.set(dom_prefix + 'ws_enableEarlyData', true);
opt.set(dom_prefix + 'ws_maxEarlyData', ws_path_params.ed);
opt.set(dom_prefix + 'ws_earlyDataHeaderName', 'Sec-WebSocket-Protocol');
}
}
} else if (ssm.net === "http") { } else if (ssm.net === "http") {
if (dom_prefix == "xray_") { if (dom_prefix == "xray_") {
opt.set(dom_prefix + 'xhttp_mode', "stream-one"); opt.set(dom_prefix + 'xhttp_mode', "stream-one");
@@ -1446,14 +1344,14 @@ local current_node = map:get(section)
opt.set(dom_prefix + 'use_finalmask', !!ssm.fm); opt.set(dom_prefix + 'use_finalmask', !!ssm.fm);
opt.set(dom_prefix + '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) { if (vless_type == "sing-box" && has_singbox) {
dom_prefix = "singbox_"; dom_prefix = "singbox_";
} else if (has_xray) { } else if (has_xray) {
dom_prefix = "xray_"; dom_prefix = "xray_";
} }
var m = parseNodeUrl(ssrurl); var m = parseNodeUrl(link);
var password = m.passwd; var password = m.passwd;
if (password === "") { if (password === "") {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>"; s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
@@ -1475,7 +1373,7 @@ local current_node = map:get(section)
if (["xhttp", "kcp", "mkcp"].includes(queryParam.type) && vless_type !== "xray" && has_xray) { if (["xhttp", "kcp", "mkcp"].includes(queryParam.type) && vless_type !== "xray" && has_xray) {
dom_prefix = "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 + 'protocol', "vless");
opt.set(dom_prefix + 'uuid', password); opt.set(dom_prefix + 'uuid', password);
@@ -1549,24 +1447,6 @@ local current_node = map:get(section)
} else if (queryParam.type === "ws") { } else if (queryParam.type === "ws") {
opt.set(dom_prefix + 'ws_host', queryParam.host || ""); opt.set(dom_prefix + 'ws_host', queryParam.host || "");
opt.set(dom_prefix + 'ws_path', queryParam.path || ""); opt.set(dom_prefix + 'ws_path', queryParam.path || "");
if (dom_prefix == "singbox_" && queryParam.path && queryParam.path.length > 1) {
var ws_path_params = {};
var ws_path_dat = queryParam.path.split('?');
var ws_path = ws_path_dat[0];
var ws_path_params = {};
var ws_path_params_array = (ws_path_dat[1] || '').split('&');
for (i = 0; i < ws_path_params_array.length; i++) {
var kv = ws_path_params_array[i].split('=');
ws_path_params[decodeURIComponent(kv[0]).toLowerCase()] = decodeURIComponent(kv[1] || '');
}
if (ws_path_params.ed) {
opt.set(dom_prefix + 'ws_path', ws_path);
opt.set(dom_prefix + 'ws_enableEarlyData', true);
opt.set(dom_prefix + 'ws_maxEarlyData', ws_path_params.ed);
opt.set(dom_prefix + 'ws_earlyDataHeaderName', 'Sec-WebSocket-Protocol');
}
}
} else if (queryParam.type === "h2" || queryParam.type === "http") { } else if (queryParam.type === "h2" || queryParam.type === "http") {
if (dom_prefix == "xray_") { if (dom_prefix == "xray_") {
opt.set(dom_prefix + 'xhttp_mode', "stream-one"); opt.set(dom_prefix + 'xhttp_mode', "stream-one");
@@ -1605,8 +1485,8 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(m.hash.substr(1))); opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
} }
} }
if (ssu[0] === "hysteria2" || ssu[0] === "hy2") { if (link_type === "hysteria2" || link_type === "hy2") {
var m = parseNodeUrl(ssrurl); var m = parseNodeUrl(link);
var password = m.passwd; var password = m.passwd;
if (password === "") { if (password === "") {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>"; s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
@@ -1671,13 +1551,13 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(m.hash.substr(1))); opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
} }
} }
if (ssu[0] === "tuic") { if (link_type === "tuic") {
if (has_singbox) { if (has_singbox) {
dom_prefix = "singbox_" dom_prefix = "singbox_";
opt.set('type', "sing-box"); opt.set('type', "sing-box");
} }
opt.set(dom_prefix + 'protocol', "tuic"); 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 username = _parsedUrl.username;
var password = _parsedUrl.password; var password = _parsedUrl.password;
var hostname = _parsedUrl.hostname; var hostname = _parsedUrl.hostname;
@@ -1720,13 +1600,13 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(hash.substr(1))); opt.set('remarks', decodeURIComponent(hash.substr(1)));
} }
} }
if (ssu[0] === "anytls") { if (link_type === "anytls") {
if (has_singbox) { if (has_singbox) {
dom_prefix = "singbox_" dom_prefix = "singbox_";
opt.set('type', "sing-box"); opt.set('type', "sing-box");
} }
opt.set(dom_prefix + 'protocol', "anytls"); opt.set(dom_prefix + 'protocol', "anytls");
var m = parseNodeUrl(ssrurl); var m = parseNodeUrl(link);
var password = m.passwd; var password = m.passwd;
if (password === "") { if (password === "") {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>"; s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
@@ -1776,13 +1656,13 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(m.hash.substr(1))); 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) { if (has_singbox) {
dom_prefix = "singbox_" dom_prefix = "singbox_";
opt.set('type', "sing-box"); opt.set('type', "sing-box");
} }
opt.set(dom_prefix + 'protocol', "naive"); 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 username = _parsedUrl.username;
var password = _parsedUrl.password; var password = _parsedUrl.password;
var hostname = _parsedUrl.hostname; var hostname = _parsedUrl.hostname;
@@ -1817,7 +1697,7 @@ local current_node = map:get(section)
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || ''); opt.set(dom_prefix + 'tls_serverName', queryParam.sni || '');
opt.set(dom_prefix + 'ech', !!queryParam.ech); opt.set(dom_prefix + 'ech', !!queryParam.ech);
opt.set(dom_prefix + 'ech_config', 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_quic', true);
opt.set(dom_prefix + 'naive_congestion_control', queryParam.congestion_control || 'bbr'); opt.set(dom_prefix + 'naive_congestion_control', queryParam.congestion_control || 'bbr');
} }
@@ -1825,16 +1705,25 @@ local current_node = map:get(section)
opt.set('remarks', decodeURIComponent(hash.substr(1))); opt.set('remarks', decodeURIComponent(hash.substr(1)));
} }
} }
if (dom_prefix && dom_prefix != null) {
if (opt.get(dom_prefix + 'port').value) { if (switch_type) {
opt.get(dom_prefix + 'port').focus(); sessionStorage.setItem("fromUrl", JSON.stringify({
opt.get(dom_prefix + 'port').blur(); timestamp: Date.now(),
savetime: 60 * 1000,
link: link
}));
opt.set("type", switch_type);
return
} }
} else { if (!dom_prefix) {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>"; s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
sessionStorage.removeItem("fromUrl"); sessionStorage.removeItem("fromUrl");
return false; 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>"; s.innerHTML = "<font color='green'><%:Import Finished %></font>";
sessionStorage.removeItem("fromUrl"); sessionStorage.removeItem("fromUrl");
return true; return true;
@@ -1844,17 +1733,6 @@ local current_node = map:get(section)
window.open('<%=api.url("gen_client_config")%>?id=' + sid, "_blank") 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> //]]></script>
<style> <style>
.share-toolbar { .share-toolbar {
@@ -1887,7 +1765,7 @@ local current_node = map:get(section)
<div class="share-toolbar"> <div class="share-toolbar">
<div class="toolbar-buttons"> <div class="toolbar-buttons">
<% if not current_node.protocol or not current_node.protocol:find("^_") then -%> <% 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="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%>')" /> <input id="genQrcodeBtn" type="button" class="btn cbi-button cbi-button-apply" value='<%:Generate QRCode%>' onclick="return genQrcode(this, '<%=self.option%>', '<%=self.value%>')" />
<% end -%> <% end -%>
@@ -5,20 +5,21 @@ local appname = map.config
local is_js_luci = api.is_js_luci() local is_js_luci = api.is_js_luci()
local section_tag = is_js_luci and "div" or "fieldset" local section_tag = is_js_luci and "div" or "fieldset"
local default_node_type = ""
local shunt_rule_list = {} local shunt_rule_list = {}
local node = map:get("@global[0]", "node") local node = map:get("@global[0]", "node")
if node then if node then
local node_type = map:get(node, "type")
local node_protocol = map:get(node, "protocol") local node_protocol = map:get(node, "protocol")
if (node_type == "Xray" or node_type == "sing-box") and node_protocol == "_shunt" then if node_protocol == "_shunt" then
default_node_type = node_protocol
local node_shunt_group = map:get(node, "shunt_group") local node_shunt_group = map:get(node, "shunt_group")
map:foreach("shunt_rules", function(e) map:foreach("shunt_rules", function(e)
if e[".name"] and e.remarks and e.group == node_shunt_group then if e[".name"] and e.remarks and e.group == node_shunt_group then
shunt_rule_list[#shunt_rule_list + 1] = e shunt_rule_list[#shunt_rule_list + 1] = e
end end
end) end)
shunt_rule_list[#shunt_rule_list + 1] = {
[".name"] = "default_node",
remarks = translate("Default")
}
end end
end end
-%> -%>
@@ -1401,6 +1402,7 @@ table td, .table .td {
var node_tr_html = ""; var node_tr_html = "";
for (var i = 0; i < group_nodes[group].length; i++) { for (var i = 0; i < group_nodes[group].length; i++) {
let o = group_nodes[group][i] let o = group_nodes[group][i]
let _port = o["port"] || o["hysteria_hop"] || o["hysteria2_hop"];
var newDom = node_template.cloneNode(true); var newDom = node_template.cloneNode(true);
newDom.classList.add("cbi-rowstyle-" + (i % 2 + 1)); newDom.classList.add("cbi-rowstyle-" + (i % 2 + 1));
var innerHTML = newDom.innerHTML; var innerHTML = newDom.innerHTML;
@@ -1414,17 +1416,15 @@ table td, .table .td {
} else { } else {
innerHTML = innerHTML.split("{{tcping}}").join('<span class="tcping_value" cbiid="{{id}}"><a href="javascript:void(0)" style="color:inherit">---</a></span>'); 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["address"] && _port) {
if (o["protocol"] === '_shunt' || is_realm) {
innerHTML = innerHTML.split("{{url_test}}").join('<span class="ping" cbiid="{{id}}">---</span>');
} else {
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>'); 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("{{id}}").join(o[".name"]);
innerHTML = innerHTML.split("{{group}}").join(o["group"] || ""); innerHTML = innerHTML.split("{{group}}").join(o["group"] || "");
let node_remarks = get_remarks_name(o); let node_remarks = get_remarks_name(o);
if (show_node_info == "1") { if (show_node_info == "1") {
let _port = o["port"] || o["hysteria_hop"] || o["hysteria2_hop"];
if (_port) _port = _port.replace(/:/g, '-'); if (_port) _port = _port.replace(/:/g, '-');
if (o["address"] && _port) { if (o["address"] && _port) {
let _address = o["address"] 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%>" /> <input class="btn cbi-button" type="button" onclick="set_node_div.set('@global[0]', 'node')" value="<%:OK%>" />
</div> </div>
</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 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 class="cbi-section-descr" style="text-align: left;"><a style="color:#FF8C00"><%:Set as the routing rule node:%></a></div>
<div> <div>
<% for i, v in ipairs(shunt_rule_list) do %> <% 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%>" /> <input class="btn cbi-button" type="button" onclick="set_node_div.set('<%=node%>', '<%=v[".name"]%>')" value="<%=v.remarks%>" />
<% end %> <% end %>
<input class="btn cbi-button" type="button" onclick="set_node_div.set('<%=node%>', 'default_node')" value="<%:Default%>" />
</div> </div>
</div> </div>
<% end %> <% end %>
+3 -36
View File
@@ -1253,6 +1253,9 @@ msgstr "访问控制"
msgid "ACLs is a tools which used to designate specific IP proxy mode." msgid "ACLs is a tools which used to designate specific IP proxy mode."
msgstr "访问控制列表是用于指定特殊 IP 代理模式的工具。" msgstr "访问控制列表是用于指定特殊 IP 代理模式的工具。"
msgid "Use global configuration when using the global node."
msgstr "与全局节点相同时,将使用全局配置。"
msgid "Example:" msgid "Example:"
msgstr "例:" msgstr "例:"
@@ -1640,36 +1643,6 @@ msgstr "启用节点日志"
msgid "Log Level" msgid "Log Level"
msgstr "日志等级" 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" msgid "Not enabled log"
msgstr "未启用日志" msgstr "未启用日志"
@@ -1742,12 +1715,6 @@ msgstr "XUDP 最大并发连接数"
msgid "Padding" msgid "Padding"
msgstr "填充" msgstr "填充"
msgid "Enable early data"
msgstr "启用前置数据"
msgid "Early data length"
msgstr "前置数据最大长度"
msgid "Early data header name" msgid "Early data header name"
msgstr "前置数据 HTTP 头名" msgstr "前置数据 HTTP 头名"
@@ -1,17 +1,12 @@
#!/bin/sh #!/bin/sh
# Devices without a hardware RTC boot with a wrong system clock: sysfixtime can [ "$ACTION" = "step" ] || exit 0
# 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" = "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 . /usr/share/passwall/utils.sh
@@ -23,5 +18,5 @@ NTP_LOCK_FILE="${LOCK_PATH}/${CONFIG}_ntp.lock"
echo $$ > ${NTP_LOCK_FILE} echo $$ > ${NTP_LOCK_FILE}
/etc/init.d/${CONFIG} restart >/dev/null 2>&1 & /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)"
} }
@@ -87,7 +87,7 @@ run_ipt2socks() {
run_singbox() { 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 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 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 "$@" eval_set_val "$@"
[ -z "$type" ] && { [ -z "$type" ] && {
type=$(echo $(config_n_get $node type) | tr 'A-Z' 'a-z') 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" [ "$remote_fakedns" = "1" ] && json_add_string "remote_dns_fake" "1"
[ -n "$remote_rewrite_ttl" ] && json_add_string "remote_rewrite_ttl" "${remote_rewrite_ttl}" [ -n "$remote_rewrite_ttl" ] && json_add_string "remote_rewrite_ttl" "${remote_rewrite_ttl}"
[ -n "$no_run" ] && json_add_string "no_run" "1" [ -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)" local _json_arg="$(json_dump)"
lua $UTIL_SINGBOX gen_config "${_json_arg}" > $config_file lua $UTIL_SINGBOX gen_config "${_json_arg}" > $config_file
[ -n "$no_run" ] && return [ -n "$no_run" ] && return
local test_log_file=$log_file local test_log_file=$log_file
local status=0
[ "$test_log_file" = "/dev/null" ] && test_log_file="${TMP_PATH}/${config_file##*/}_test.log" [ "$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 if [ "${status}" = 0 ]; then
ln_run "$SINGBOX_BIN" "sing-box" "${log_file}" run -c "$config_file" ln_run "$SINGBOX_BIN" "sing-box" "${log_file}" run -c "$config_file"
else else
@@ -201,7 +205,7 @@ run_singbox() {
run_xray() { 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 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 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 "$@" eval_set_val "$@"
[ -z "$type" ] && { [ -z "$type" ] && {
type=$(echo $(config_n_get $node type) | tr 'A-Z' 'a-z') type=$(echo $(config_n_get $node type) | tr 'A-Z' 'a-z')
@@ -278,13 +282,17 @@ run_xray() {
json_add_string "loglevel" "$loglevel" json_add_string "loglevel" "$loglevel"
[ -n "$no_run" ] && json_add_string "no_run" "1" [ -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)" local _json_arg="$(json_dump)"
lua $UTIL_XRAY gen_config "${_json_arg}" > $config_file lua $UTIL_XRAY gen_config "${_json_arg}" > $config_file
[ -n "$no_run" ] && return [ -n "$no_run" ] && return
local test_log_file=$log_file local test_log_file=$log_file
local status=0
[ "$test_log_file" = "/dev/null" ] && test_log_file="${TMP_PATH}/${config_file##*/}_test.log" [ "$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 if [ "${status}" = 0 ]; then
ln_run "$XRAY_BIN" "xray" "${log_file}" run -c "$config_file" ln_run "$XRAY_BIN" "xray" "${log_file}" run -c "$config_file"
else else
@@ -337,10 +345,10 @@ run_socks() {
[ -n "$config_file" ] && [ -z "$(echo ${config_file} | grep $TMP_PATH)" ] && config_file=$TMP_PATH/$config_file [ -n "$config_file" ] && [ -z "$(echo ${config_file} | grep $TMP_PATH)" ] && config_file=$TMP_PATH/$config_file
[ -n "$http_port" ] || http_port=0 [ -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 [ -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 if [ -z "$log_file" ] || [ "$log_file" = "/dev/null" ]; then
log_file=$TMP_PATH/$log_file
else
log_file="/dev/null" log_file="/dev/null"
elif [ "${log_file#"$TMP_PATH/"}" = "$log_file" ]; then
log_file=$TMP_PATH/$log_file
fi fi
local type=$(echo $(config_n_get $node type) | tr 'A-Z' 'a-z') local type=$(echo $(config_n_get $node type) | tr 'A-Z' 'a-z')
@@ -489,8 +497,6 @@ run_socks() {
;; ;;
esac esac
set_cache_var "node_${node}_socks_port" "${socks_port}"
# http to socks # http to socks
[ -z "$http_flag" ] && [ "$http_port" != "0" ] && [ -n "$http_config_file" ] && [ "$type" != "sing-box" ] && [ "$type" != "xray" ] && [ "$type" != "socks" ] && { [ -z "$http_flag" ] && [ "$http_port" != "0" ] && [ -n "$http_config_file" ] && [ "$type" != "sing-box" ] && [ "$type" != "xray" ] && [ "$type" != "socks" ] && {
json_init 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_local=$(config_n_get @global[0] node_socks_bind_local 1)
local node_socks_bind="127.0.0.1" local node_socks_bind="127.0.0.1"
[ "${node_socks_bind_local}" != "1" ] && node_socks_bind="0.0.0.0" [ "${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=$(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_HTTP_port=$(config_n_get @global[0] node_http_port 0) GLOBAL_HTTP_port=$(config_n_get @global[0] node_http_port 0)
[ "$GLOBAL_HTTP_port" != "0" ] && local on_node_http=1 [ "$GLOBAL_HTTP_port" != "0" ] && local on_node_http=1
if [ $PROXY_IPV6 = "1" ]; then if [ $PROXY_IPV6 = "1" ]; then
@@ -605,7 +607,7 @@ start_global() {
;; ;;
sing-box) sing-box)
local _flag="global" local _flag="global"
local _args="" local _args="run_in_global=1"
[ "$on_node_socks" = "1" ] && { [ "$on_node_socks" = "1" ] && {
node_socks_flag=1 node_socks_flag=1
_args="${_args} socks_address=${node_socks_bind} socks_port=${GLOBAL_SOCKS_port}" _args="${_args} socks_address=${node_socks_bind} socks_port=${GLOBAL_SOCKS_port}"
@@ -683,7 +685,7 @@ start_global() {
;; ;;
xray) xray)
local _flag="global" local _flag="global"
local _args="" local _args="run_in_global=1"
[ "$on_node_socks" = "1" ] && { [ "$on_node_socks" = "1" ] && {
node_socks_flag=1 node_socks_flag=1
_args="${_args} socks_address=${node_socks_bind} socks_port=${GLOBAL_SOCKS_port}" _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}" set_cache_var "GLOBAL_SOCKS_server" "${GLOBAL_SOCKS_server}"
} }
[ "$type" != "sing-box" ] && [ "$type" != "xray" ] && echo "${NODE}" >> $TMP_PATH/direct_node_list [ "$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_node" "$NODE"
set_cache_var "ACL_GLOBAL_redir_port" "$REDIR_PORT" set_cache_var "ACL_GLOBAL_redir_port" "$REDIR_PORT"
} }
@@ -1382,6 +1383,7 @@ acl_app() {
log=${log:-0} log=${log:-0}
loglevel=${loglevel:-warn} loglevel=${loglevel:-warn}
log_chinadns_ng=${log_chinadns_ng:-0}
if [ -n "${sources}" ]; then if [ -n "${sources}" ]; then
for s in $sources; do for s in $sources; do
@@ -1437,7 +1439,10 @@ acl_app() {
[ -n "$node" ] && { [ -n "$node" ] && {
local GLOBAL_NODE=$(get_cache_var "ACL_GLOBAL_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 [ "$node" = "default" ]; then
if [ -n "${GLOBAL_NODE}" ]; then if [ -n "${GLOBAL_NODE}" ]; then
set_cache_var "ACL_${sid}_node" "${GLOBAL_NODE}" set_cache_var "ACL_${sid}_node" "${GLOBAL_NODE}"
@@ -1449,14 +1454,7 @@ acl_app() {
echolog " - 全局节点未启用,跳过【${remarks}" echolog " - 全局节点未启用,跳过【${remarks}"
fi fi
else else
[ "$(config_get_type $node)" = "nodes" ] || [ "$(config_get_type $node)" = "socks" ] && { ([ "$(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
else
local type protocol local type protocol
if [ "$(config_get_type $node)" = "socks" ]; then if [ "$(config_get_type $node)" = "socks" ]; then
if [ "${dns_mode}" = "xray" ]; then if [ "${dns_mode}" = "xray" ]; then
@@ -1482,23 +1480,21 @@ acl_app() {
esac 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" ]) && { 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="${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}" 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 if [ "$remote_fakedns" = "1" ] || ([ "$protocol" = "_shunt" ] && [ "$(config_n_get $node fakedns)" = "1" ]); then
use_fakedns=1 use_fakedns=1
fi fi
run_dns() { run_dns() {
local _dns_port local dns_fwd_port=$1
[ -n $1 ] && _dns_port=$1 [ -z "$dns_fwd_port" ] && {
[ -z ${_dns_port} ] && {
dns_port=$(get_new_port $(expr $dns_port + 1)) dns_port=$(get_new_port $(expr $dns_port + 1))
_dns_port=$dns_port dns_fwd_port=$dns_port
if [ "$dns_mode" = "dns2socks" ]; then 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 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 elif [ "$dns_mode" = "sing-box" ] || [ "$dns_mode" = "xray" ]; then
config_file=$TMP_ACL_PATH/${node}_SOCKS_${socks_port}_DNS.json config_file=$TMP_ACL_PATH/${node}_SOCKS_${socks_port}_DNS.json
remote_dns_doh=${remote_dns_doh:-https://1.1.1.1/dns-query} remote_dns_doh=${remote_dns_doh:-https://1.1.1.1/dns-query}
@@ -1506,10 +1502,15 @@ acl_app() {
[ "${dns_mode}" = "sing-box" ] && type="singbox" [ "${dns_mode}" = "sing-box" ] && type="singbox"
dnsmasq_filter_proxy_ipv6=0 dnsmasq_filter_proxy_ipv6=0
remote_dns_query_strategy="UseIP" remote_dns_query_strategy="UseIP"
[ "$filter_proxy_ipv6" = "1" ] && remote_dns_query_strategy="UseIPv4" [ "$filter_proxy_ipv6" = "1" ] && {
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 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 fi
set_cache_var "node_${node}_$(echo -n "${dns_cache_key}" | md5sum | cut -d " " -f1)" "${_dns_port}" set_cache_var "node_${node}_${dns_cache_key}" "$dns_fwd_port"
} }
[ "$dns_shunt" = "chinadns-ng" ] && [ -n "$(first_type chinadns-ng)" ] && { [ "$dns_shunt" = "chinadns-ng" ] && [ -n "$(first_type chinadns-ng)" ] && {
@@ -1538,7 +1539,7 @@ acl_app() {
_flag="$sid" \ _flag="$sid" \
_listen_port=${chinadns_port} \ _listen_port=${chinadns_port} \
_dns_local=${_chinadns_local_dns} \ _dns_local=${_chinadns_local_dns} \
_dns_trust=127.0.0.1#${_dns_port} \ _dns_trust=127.0.0.1#${dns_fwd_port} \
_no_ipv6_trust=${filter_proxy_ipv6} \ _no_ipv6_trust=${filter_proxy_ipv6} \
_use_direct_list=${use_direct_list} \ _use_direct_list=${use_direct_list} \
_use_proxy_list=${use_proxy_list} \ _use_proxy_list=${use_proxy_list} \
@@ -1550,46 +1551,50 @@ acl_app() {
_no_logic_log=1 \ _no_logic_log=1 \
_node=${node} \ _node=${node} \
_filter_https=${force_https_soa:-0} \ _filter_https=${force_https_soa:-0} \
_log=${log} _log=${log_chinadns_ng}
use_default_dns="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)) dnsmasq_port=$(get_new_port $(expr $dnsmasq_port + 1))
local dnsmasq_conf=${acl_path}/dnsmasq.conf local dnsmasq_conf=${acl_path}/dnsmasq.conf
local dnsmasq_conf_path=${acl_path}/dnsmasq.d 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} \ 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 \ -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}" \ -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} \ -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} \ -NODE $node -DEFAULT_PROXY_MODE ${tcp_proxy_mode} -NO_PROXY_IPV6 ${dnsmasq_filter_proxy_ipv6:-0} -NFTFLAG ${nftflag:-0} \
-NO_LOGIC_LOG 1 -NO_LOGIC_LOG 1
ln_run "$(first_type dnsmasq)" "dnsmasq_${sid}" "/dev/null" -C ${dnsmasq_conf} -x ${acl_path}/dnsmasq.pid 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 "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 #dhcp.leases to hosts
$APP_PATH/lease2hosts.sh > /dev/null 2>&1 & $APP_PATH/lease2hosts.sh > /dev/null 2>&1 &
} }
_redir_port=$(get_cache_var "node_${node}_redir_port") local _redir_port=$(get_cache_var "acl_node_${node}_redir_port")
_socks_port=$(get_cache_var "node_${node}_socks_port") local _socks_port=$(get_cache_var "acl_node_${node}_socks_port")
if [ -n "${_socks_port}" ] && [ -n "${_redir_port}" ]; then 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} socks_port=${_socks_port}
node_port=${_redir_port} node_port=${_redir_port}
_dnsmasq_port=$(get_cache_var "node_${node}_$(echo -n "${tcp_proxy_mode}_${dns_cache_key}" | md5sum | cut -d " " -f1)") _dns_port=$(get_cache_var "node_${node}_${dns_cache_key}")
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} run_dns ${_dns_port}
else
[ -n "${_dnsmasq_port}" ] && set_cache_var "ACL_${sid}_dns_port" "${_dnsmasq_port}"
fi
else else
socks_port=$(get_new_port $(expr $socks_port + 1)) socks_port=$(get_new_port $(expr $socks_port + 1))
set_cache_var "node_${node}_socks_port" "${socks_port}" set_cache_var "acl_node_${node}_socks_port" "${socks_port}"
redir_port=$(get_new_port $(expr $redir_port + 1)) redir_port=$(get_new_port $(expr $redir_port + 1))
set_cache_var "node_${node}_redir_port" "${redir_port}" set_cache_var "acl_node_${node}_redir_port" "${redir_port}"
node_port=$redir_port node_port=$redir_port
local log_file="/dev/null" local log_file="/dev/null"
[ "${log}" = "1" ] && log_file="${TMP_ACL_PATH}/${sid}/node.log" [ "${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 if [ "${type}" = "sing-box" ] || [ "${type}" = "xray" ]; then
config_file="acl/${node}_${redir_port}.json" config_file="acl/${node}_${redir_port}.json"
@@ -1599,7 +1604,10 @@ acl_app() {
_dns_port=$dns_port _dns_port=$dns_port
dnsmasq_filter_proxy_ipv6=0 dnsmasq_filter_proxy_ipv6=0
remote_dns_query_strategy="UseIP" remote_dns_query_strategy="UseIP"
[ "$filter_proxy_ipv6" = "1" ] && remote_dns_query_strategy="UseIPv4" [ "$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} 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} 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} 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}"
@@ -1623,13 +1631,12 @@ acl_app() {
fi fi
set_cache_var "ACL_${sid}_node" "${node}" set_cache_var "ACL_${sid}_node" "${node}"
set_cache_var "ACL_${sid}_redir_port" "${node_port}" set_cache_var "ACL_${sid}_redir_port" "${node_port}"
fi
} }
fi fi
[ "${use_fakedns}" = "1" ] && set_cache_var "ACL_${sid}_fakedns" "1" [ "${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 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 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 done
unset socks_port redir_port dns_port dnsmasq_port chinadns_port 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 RESOLVFILE=/tmp/resolv.conf.d/resolv.conf.auto
[ -f "${RESOLVFILE}" ] && [ -s "${RESOLVFILE}" ] || RESOLVFILE=/tmp/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_DNS=$(grep -Eo "$IPv4_REGEX" "$RESOLVFILE" 2>/dev/null | grep -Ev '^(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_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') 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/,$//') [ -z "${DEFAULT_DNS}" ] && [ "$(echo $ISP_DNS | tr ' ' '\n' | wc -l)" -ge 1 ] && DEFAULT_DNS=$(echo -n $ISP_DNS | tr ' ' '\n' | tr '\n' ',' | sed 's/,$//')
@@ -88,7 +88,16 @@ local function build_common(node)
if net == "ws" then if net == "ws" then
local opts = node["ws-opts"] local opts = node["ws-opts"]
if opts then if opts then
o.transport.path = opts.path local path = opts.path or "/"
local ed = opts["max-early-data"]
local eh = opts["early-data-header-name"]
if ed then
path = path .. "?ed=" .. ed
end
if eh then
path = path .. (path:find("?", 1, true) and "&eh=" or "?eh=") .. eh
end
o.transport.path = path
o.transport.host = opts.headers and opts.headers.Host o.transport.host = opts.headers and opts.headers.Host
end end
@@ -109,8 +109,12 @@ api.uci_foreach_c("haproxy_config", function(t)
t.origin_port = server_port t.origin_port = server_port
if health_check_type == "script_logic" then if health_check_type == "script_logic" then
if server_node.type ~= "Socks" then if server_node.type ~= "Socks" then
local relay_port = server_node.port local new_port
local new_port = api.get_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) 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', sys.call(string.format('/usr/share/%s/app.sh run_socks "%s"> /dev/null',
appname, appname,
@@ -123,6 +127,8 @@ api.uci_foreach_c("haproxy_config", function(t)
) )
) )
) )
api.set_socks_port_to_cache(server_node[".name"], new_port)
end
server_address = "127.0.0.1" server_address = "127.0.0.1"
server_port = new_port server_port = new_port
end 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}_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 "$(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" ] && { [ -n "$node" ] && {
if [ "$(config_get_type $node)" = "socks" ]; then if [ "$(config_get_type $node)" = "socks" ]; then
node_remark="Socks 配置($(config_n_get $node port) 端口)" node_remark="Socks 配置($(config_n_get $node port) 端口)"
@@ -385,16 +391,16 @@ load_acl() {
local shunt_id local shunt_id
for shunt_id in $shunt_ids; do for shunt_id in $shunt_ids; do
[ "${shunt_group}" != "$(config_n_get ${shunt_id} group)" ] && continue [ "${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" | 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" | 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 "$IPv6_REGEX" | sed -e "s/^/add $shunt6_set_name &/g" -e "s/$/ timeout 0/g" | ipset -! -R
[ "$USE_GEOVIEW" = "1" ] && { [ "$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') 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" [ -n "$geoip_code" ] && GEOIP_CODE="${GEOIP_CODE:+$GEOIP_CODE,}$geoip_code"
} }
done done
if [ -n "$GEOIP_CODE" ]; then 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 ipv4 | 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 ipv6 | sed -e "s/^/add $shunt6_set_name &/g" -e "s/$/ timeout 0/g" | ipset -! -R
fi fi
} }
} }
@@ -402,16 +408,16 @@ load_acl() {
else else
[ -n "${DIRECT_DNSMASQ_PORT}" ] && dns_redirect=${DIRECT_DNSMASQ_PORT} [ -n "${DIRECT_DNSMASQ_PORT}" ] && dns_redirect=${DIRECT_DNSMASQ_PORT}
fi fi
if [ -n "${dns_redirect}" ]; then if ([ -n "$tcp_port" ] || [ -n "$udp_port" ]) && [ -n "$dns_redirect" ]; then
$ipt_m -A PSW $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j RETURN $ipt_m -A PSW $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j RETURN
[ "$_ipv4" != "1" ] && $ip6t_m -A PSW $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j RETURN 2>/dev/null $ip6t_m -A PSW $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j RETURN 2>/dev/null
$ipt_m -A PSW $(comment "$remarks") -p tcp ${_ipt_source} --dport 53 -j RETURN $ipt_m -A PSW $(comment "$remarks") -p tcp ${_ipt_source} --dport 53 -j RETURN
[ "$_ipv4" != "1" ] && $ip6t_m -A PSW $(comment "$remarks") -p tcp ${_ipt_source} --dport 53 -j RETURN 2>/dev/null $ip6t_m -A PSW $(comment "$remarks") -p tcp ${_ipt_source} --dport 53 -j RETURN 2>/dev/null
$ipt_n -A PSW_DNS $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j REDIRECT --to-ports ${dns_redirect} $ipt_n -A PSW_DNS $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j REDIRECT --to-ports ${dns_redirect}
[ "$_ipv4" != "1" ] && $ip6t_n -A PSW_DNS $(comment "$remarks") -p udp ${_ipt_source} --dport 53 -j REDIRECT --to-ports ${dns_redirect} 2>/dev/null $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} $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 $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 fi
[ -n "$tcp_port" ] || [ -n "$udp_port" ] && { [ -n "$tcp_port" ] || [ -n "$udp_port" ] && {
@@ -591,7 +597,7 @@ load_acl() {
[ -n "${DIRECT_DNSMASQ_PORT}" ] && DNS_REDIRECT=${DIRECT_DNSMASQ_PORT} [ -n "${DIRECT_DNSMASQ_PORT}" ] && DNS_REDIRECT=${DIRECT_DNSMASQ_PORT}
fi fi
if [ -n "${DNS_REDIRECT}" ]; then if ([ -n "${TCP_PROXY_MODE}" ] || [ -n "${UDP_PROXY_MODE}" ]) && [ -n "$DNS_REDIRECT" ]; then
$ipt_m -A PSW $(comment "默认") -p udp --dport 53 -j RETURN $ipt_m -A PSW $(comment "默认") -p udp --dport 53 -j RETURN
$ip6t_m -A PSW $(comment "默认") -p udp --dport 53 -j RETURN 2>/dev/null $ip6t_m -A PSW $(comment "默认") -p udp --dport 53 -j RETURN 2>/dev/null
$ipt_m -A PSW $(comment "默认") -p tcp --dport 53 -j RETURN $ipt_m -A PSW $(comment "默认") -p tcp --dport 53 -j RETURN
@@ -600,6 +606,7 @@ load_acl() {
$ip6t_n -A PSW_DNS $(comment "默认") -p udp --dport 53 -j REDIRECT --to-ports ${DNS_REDIRECT} 2>/dev/null $ip6t_n -A PSW_DNS $(comment "默认") -p udp --dport 53 -j REDIRECT --to-ports ${DNS_REDIRECT} 2>/dev/null
$ipt_n -A PSW_DNS $(comment "默认") -p tcp --dport 53 -j REDIRECT --to-ports ${DNS_REDIRECT} $ipt_n -A PSW_DNS $(comment "默认") -p tcp --dport 53 -j REDIRECT --to-ports ${DNS_REDIRECT}
$ip6t_n -A PSW_DNS $(comment "默认") -p tcp --dport 53 -j REDIRECT --to-ports ${DNS_REDIRECT} 2>/dev/null $ip6t_n -A PSW_DNS $(comment "默认") -p tcp --dport 53 -j REDIRECT --to-ports ${DNS_REDIRECT} 2>/dev/null
echolog " - ${msg}DNS 重定向到专用服务器[${DNS_REDIRECT}]"
fi fi
[ -n "${TCP_PROXY_MODE}" ] || [ -n "${UDP_PROXY_MODE}" ] && { [ -n "${TCP_PROXY_MODE}" ] || [ -n "${UDP_PROXY_MODE}" ] && {
@@ -757,12 +764,12 @@ filter_haproxy() {
filter_vpsip() { 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 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" ] && { [ -n "$ipv4_addrs" ] && {
echo "$ipv4_addrs" | sed "s/^/add $IPSET_VPS /" | awk '1; END{print "COMMIT"}' | ipset -! -R echo "$ipv4_addrs" | sed "s/^/add $IPSET_VPS /" | awk '1; END{print "COMMIT"}' | ipset -! -R
echolog " - [$?]加入所有IPv4节点服务器IP到ipset[$IPSET_VPS]直连完成" 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" ] && { [ -n "$ipv6_addrs" ] && {
echo "$ipv6_addrs" | sed "s/^/add $IPSET_VPS6 /" | awk '1; END{print "COMMIT"}' | ipset -! -R echo "$ipv6_addrs" | sed "s/^/add $IPSET_VPS6 /" | awk '1; END{print "COMMIT"}' | ipset -! -R
echolog " - [$?]加入所有IPv6节点服务器IP到ipset[$IPSET_VPS6]直连完成" echolog " - [$?]加入所有IPv6节点服务器IP到ipset[$IPSET_VPS6]直连完成"
@@ -770,8 +777,8 @@ filter_vpsip() {
#订阅方式为直连时 #订阅方式为直连时
local subscribe_host=$(get_subscribe_host | grep -Ev "$EXCLUDE_VPSIP") local subscribe_host=$(get_subscribe_host | grep -Ev "$EXCLUDE_VPSIP")
[ -n "$subscribe_host" ] && { [ -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 "$IPv4_REGEX" | 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 "$IPv6_REGEX" | sed "s/^/add $IPSET_VPS6 /" | awk '{print $0} END{print "COMMIT"}' | ipset -! -R
} }
} }
@@ -779,8 +786,8 @@ filter_server_port() {
local address="$1" local address="$1"
local port=$(echo "$2" | tr '-' ':' | tr -d ' ') local port=$(echo "$2" | tr '-' ':' | tr -d ' ')
local stream=$(echo "$3" | tr 'A-Z' 'a-z') local stream=$(echo "$3" | tr 'A-Z' 'a-z')
local ipt_tmp="$ipt_n" _is_tproxy _ipt_cmd _ver multi_ports p ports local _is_tproxy="$4"
[ "$(config_n_get @global_forwarding[0] tcp_proxy_way redirect)" = "tproxy" ] && _is_tproxy="TPROXY" local ipt_tmp="$ipt_n" _ipt_cmd _ver multi_ports p ports
[ "$stream" = "udp" ] && _is_tproxy="TPROXY" [ "$stream" = "udp" ] && _is_tproxy="TPROXY"
[ -n "$_is_tproxy" ] && ipt_tmp="$ipt_m" [ -n "$_is_tproxy" ] && ipt_tmp="$ipt_m"
for _ver in 4 6; do for _ver in 4 6; do
@@ -807,7 +814,7 @@ filter_server_port() {
} }
filter_node() { filter_node() {
local node="$1" stream="$2" local node="$1" stream="$2" _is_tproxy="$3"
[ -z "$node" ] && return 1 [ -z "$node" ] && return 1
local address=$(config_n_get "$node" address) local address=$(config_n_get "$node" address)
local port=$(config_n_get "$node" port) local port=$(config_n_get "$node" port)
@@ -816,13 +823,15 @@ filter_node() {
[ -z "$address" ] && return 1 [ -z "$address" ] && return 1
echo "$address" | grep -Eq "$EXCLUDE_VPSIP" && return 1 echo "$address" | grep -Eq "$EXCLUDE_VPSIP" && return 1
[ -z "$port" ] && return 1 [ -z "$port" ] && return 1
filter_server_port "$address" "$port" "$stream" filter_server_port "$address" "$port" "$stream" "$_is_tproxy"
} }
filter_direct_node_list() { filter_direct_node_list() {
[ ! -s "$TMP_PATH/direct_node_list" ] && return [ ! -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 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 filter_node "$_node_id" UDP
unset _node_id unset _node_id
done done
@@ -921,13 +930,13 @@ add_firewall_rule() {
#直连列表 #直连列表
[ "$USE_DIRECT_LIST_ALL" = "1" ] && { [ "$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" | 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" | 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 "$IPv6_REGEX" | sed -e "s/^/add $IPSET_WHITE6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
[ "$USE_GEOVIEW" = "1" ] && { [ "$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') 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 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 ipv4 | 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 ipv6 | sed -e "s/^/add $IPSET_WHITE6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
echolog " - [$?]解析并加入[直连列表] GeoIP 到 IPSET 完成" echolog " - [$?]解析并加入[直连列表] GeoIP 到 IPSET 完成"
fi fi
} }
@@ -935,13 +944,13 @@ add_firewall_rule() {
#代理列表 #代理列表
[ "$USE_PROXY_LIST_ALL" = "1" ] && { [ "$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" | 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" | 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 "$IPv6_REGEX" | sed -e "s/^/add $IPSET_BLACK6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
[ "$USE_GEOVIEW" = "1" ] && { [ "$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') 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 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 ipv4 | 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 ipv6 | sed -e "s/^/add $IPSET_BLACK6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
echolog " - [$?]解析并加入[代理列表] GeoIP 到 IPSET 完成" echolog " - [$?]解析并加入[代理列表] GeoIP 到 IPSET 完成"
fi fi
} }
@@ -949,13 +958,13 @@ add_firewall_rule() {
#屏蔽列表 #屏蔽列表
[ "$USE_BLOCK_LIST_ALL" = "1" ] && { [ "$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" | 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" | 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 "$IPv6_REGEX" | sed -e "s/^/add $IPSET_BLOCK6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
[ "$USE_GEOVIEW" = "1" ] && { [ "$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') 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 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 ipv4 | 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 ipv6 | sed -e "s/^/add $IPSET_BLOCK6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
echolog " - [$?]解析并加入[屏蔽列表] GeoIP 到 IPSET 完成" echolog " - [$?]解析并加入[屏蔽列表] GeoIP 到 IPSET 完成"
fi fi
} }
@@ -969,16 +978,16 @@ add_firewall_rule() {
local shunt_id local shunt_id
for shunt_id in $shunt_ids; do for shunt_id in $shunt_ids; do
[ "${shunt_group}" != "$(config_n_get ${shunt_id} group)" ] && continue [ "${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" | 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" | 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 "$IPv6_REGEX" | sed -e "s/^/add $IPSET_SHUNT6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
[ "$USE_GEOVIEW" = "1" ] && { [ "$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') 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" [ -n "$geoip_code" ] && GEOIP_CODE="${GEOIP_CODE:+$GEOIP_CODE,}$geoip_code"
} }
done done
if [ -n "$GEOIP_CODE" ]; then 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 ipv4 | 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 ipv6 | sed -e "s/^/add $IPSET_SHUNT6 &/g" -e "s/$/ timeout 0/g" | ipset -! -R
echolog " - [$?]解析并加入[分流节点] GeoIP 到 IPSET 完成" echolog " - [$?]解析并加入[分流节点] GeoIP 到 IPSET 完成"
fi fi
} }
@@ -1090,7 +1099,7 @@ add_firewall_rule() {
insert_rule_before "$ipt_m" "PREROUTING" "mwan3" "-j PSW" insert_rule_before "$ipt_m" "PREROUTING" "mwan3" "-j PSW"
# Only TCP, UDP Invalid. # 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 -N PSW_OUTPUT
$ipt_m -A PSW_OUTPUT $(dst $IPSET_LAN) -j RETURN $ipt_m -A PSW_OUTPUT $(dst $IPSET_LAN) -j RETURN
@@ -1164,7 +1173,7 @@ add_firewall_rule() {
insert_rule_before "$ip6t_m" "PREROUTING" "mwan3" "-j PSW" insert_rule_before "$ip6t_m" "PREROUTING" "mwan3" "-j PSW"
# Only TCP, UDP Invalid. # 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 -N PSW_OUTPUT
$ip6t_m -A PSW_OUTPUT -m mark --mark 0xff/0xff -j RETURN $ip6t_m -A PSW_OUTPUT -m mark --mark 0xff/0xff -j RETURN
@@ -1469,7 +1478,7 @@ gen_include() {
[ -z "${_ipt}" ] && return [ -z "${_ipt}" ] && return
echo "*$2" 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' echo 'COMMIT'
} }
local __ipt="" local __ipt=""
@@ -1493,7 +1502,7 @@ gen_include() {
[ -z "${is_tproxy}" ] && \$(${MY_PATH} insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "-p tcp -j PSW") [ -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" "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 EOF
) )
} }
@@ -1514,7 +1523,7 @@ gen_include() {
[ "$accept_icmpv6" = "1" ] && $ip6t_n -A PREROUTING -p ipv6-icmp -j PSW [ "$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" "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 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}_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 "$(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" ] && { [ -n "$node" ] && {
if [ "$(config_get_type $node)" = "socks" ]; then if [ "$(config_get_type $node)" = "socks" ]; then
node_remark="Socks 配置($(config_n_get $node port) 端口)" node_remark="Socks 配置($(config_n_get $node port) 端口)"
@@ -446,16 +452,16 @@ load_acl() {
local shunt_id local shunt_id
for shunt_id in $shunt_ids; do for shunt_id in $shunt_ids; do
[ "${shunt_group}" != "$(config_n_get ${shunt_id} group)" ] && continue [ "${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 "$IPv4_REGEX" | 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 "$IPv6_REGEX" | insert_nftset $shunt6_set_name_static
[ "$USE_GEOVIEW" = "1" ] && { [ "$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') 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" [ -n "$geoip_code" ] && GEOIP_CODE="${GEOIP_CODE:+$GEOIP_CODE,}$geoip_code"
} }
done done
if [ -n "$GEOIP_CODE" ]; then 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 ipv4 | 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 ipv6 | insert_nftset $shunt6_set_name_static
fi fi
} }
} }
@@ -463,16 +469,15 @@ load_acl() {
else else
[ -n "${DIRECT_DNSMASQ_PORT}" ] && dns_redirect=${DIRECT_DNSMASQ_PORT} [ -n "${DIRECT_DNSMASQ_PORT}" ] && dns_redirect=${DIRECT_DNSMASQ_PORT}
fi fi
if [ -n "${dns_redirect}" ]; then
if ([ -n "$tcp_port" ] || [ -n "$udp_port" ]) && [ -n "$dns_redirect" ]; then
nft "add rule $NFTABLE_NAME PSW_MANGLE ip protocol udp ${_ipt_source} udp dport 53 counter return comment \"$remarks\"" 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\"" 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_MANGLE_V6 meta l4proto udp ${_ipt_source} udp 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_MANGLE_V6 meta l4proto tcp ${_ipt_source} tcp dport 53 counter return 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 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\""
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_redirect}]"
[ -z "$(get_cache_var "ACL_${sid}_default")" ] && echolog " - ${msg}使用与全局配置不相同节点,已将DNS强制重定向到专用 DNS 服务器。"
fi fi
[ -n "$tcp_port" ] || [ -n "$udp_port" ] && { [ -n "$tcp_port" ] || [ -n "$udp_port" ] && {
@@ -651,15 +656,14 @@ load_acl() {
[ -n "${DIRECT_DNSMASQ_PORT}" ] && DNS_REDIRECT=${DIRECT_DNSMASQ_PORT} [ -n "${DIRECT_DNSMASQ_PORT}" ] && DNS_REDIRECT=${DIRECT_DNSMASQ_PORT}
fi fi
if [ -n "${DNS_REDIRECT}" ]; then if ([ -n "${TCP_PROXY_MODE}" ] || [ -n "${UDP_PROXY_MODE}" ]) && [ -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 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 ip protocol tcp tcp 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_V6 meta l4proto 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 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 \"默认\"" nft "add rule $NFTABLE_NAME PSW_DNS meta l4proto tcp tcp dport 53 counter redirect to :${DNS_REDIRECT} comment \"默认\""
echolog " - ${msg}DNS 重定向到专用服务器[${DNS_REDIRECT}]"
fi fi
[ -n "${TCP_PROXY_MODE}" ] || [ -n "${UDP_PROXY_MODE}" ] && { [ -n "${TCP_PROXY_MODE}" ] || [ -n "${UDP_PROXY_MODE}" ] && {
@@ -829,12 +833,12 @@ filter_vps_addr() {
filter_vpsip() { 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 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" ] && { [ -n "$ipv4_addrs" ] && {
echo "$ipv4_addrs" | insert_nftset $NFTSET_VPS echo "$ipv4_addrs" | insert_nftset $NFTSET_VPS
echolog " - [$?]加入所有IPv4节点服务器IP到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" ] && { [ -n "$ipv6_addrs" ] && {
echo "$ipv6_addrs" | insert_nftset $NFTSET_VPS6 echo "$ipv6_addrs" | insert_nftset $NFTSET_VPS6
echolog " - [$?]加入所有IPv6节点服务器IP到nftset[$NFTSET_VPS6]直连完成" echolog " - [$?]加入所有IPv6节点服务器IP到nftset[$NFTSET_VPS6]直连完成"
@@ -842,8 +846,8 @@ filter_vpsip() {
#订阅方式为直连时 #订阅方式为直连时
local subscribe_host=$(get_subscribe_host | grep -Ev "$EXCLUDE_VPSIP") local subscribe_host=$(get_subscribe_host | grep -Ev "$EXCLUDE_VPSIP")
[ -n "$subscribe_host" ] && { [ -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 "$IPv4_REGEX" | 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 "$IPv6_REGEX" | insert_nftset $NFTSET_VPS6
} }
} }
@@ -851,9 +855,9 @@ filter_server_port() {
local address="$1" local address="$1"
local port=$(echo "$2" | tr ':' '-' | tr -d ' ') local port=$(echo "$2" | tr ':' '-' | tr -d ' ')
local stream=$(echo "$3" | tr 'A-Z' 'a-z') 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" 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" [ "$stream" = "udp" ] && _is_tproxy="TPROXY"
[ -n "$_is_tproxy" ] && _nft_output_chain="PSW_OUTPUT_MANGLE" [ -n "$_is_tproxy" ] && _nft_output_chain="PSW_OUTPUT_MANGLE"
case "$port" in case "$port" in
@@ -869,7 +873,7 @@ filter_server_port() {
} }
filter_node() { filter_node() {
local node="$1" stream="$2" local node="$1" stream="$2" _is_tproxy="$3"
[ -z "$node" ] && return 1 [ -z "$node" ] && return 1
local address=$(config_n_get "$node" address) local address=$(config_n_get "$node" address)
local port=$(config_n_get "$node" port) local port=$(config_n_get "$node" port)
@@ -878,13 +882,15 @@ filter_node() {
[ -z "$address" ] && return 1 [ -z "$address" ] && return 1
echo "$address" | grep -Eq "$EXCLUDE_VPSIP" && return 1 echo "$address" | grep -Eq "$EXCLUDE_VPSIP" && return 1
[ -z "$port" ] && return 1 [ -z "$port" ] && return 1
filter_server_port "$address" "$port" "$stream" filter_server_port "$address" "$port" "$stream" "$_is_tproxy"
} }
filter_direct_node_list() { filter_direct_node_list() {
[ ! -s "$TMP_PATH/direct_node_list" ] && return [ ! -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 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 filter_node "$_node_id" UDP
unset _node_id unset _node_id
done done
@@ -902,16 +908,22 @@ add_script_mwan3() {
} }
} }
MWAN3_RULE_ARGS="-m connmark --mark ${FWMARK}/0xffffffff -j RETURN"
mwan3_stop() { 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}') 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 for handle in $handles; do
nft delete rule ip mangle mwan3_hook handle ${handle} 2>/dev/null nft delete rule ip mangle mwan3_hook handle ${handle} 2>/dev/null
done done
while iptables -w 5 -t mangle -D mwan3_hook ${MWAN3_RULE_ARGS} >/dev/null 2>&1; do :; done
} }
mwan3_start() { mwan3_start() {
nft list chain ip mangle mwan3_hook >/dev/null 2>&1 || return 0
mwan3_stop 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() { update_wan_sets() {
@@ -1024,13 +1036,13 @@ add_firewall_rule() {
#直连列表 #直连列表
[ "$USE_DIRECT_LIST_ALL" = "1" ] && { [ "$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 "$IPv4_REGEX" | 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 "$IPv6_REGEX" | insert_nftset $NFTSET_WHITE6_STATIC
[ "$USE_GEOVIEW" = "1" ] && { [ "$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') 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 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 ipv4 | 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 ipv6 | insert_nftset $NFTSET_WHITE6_STATIC
echolog " - [$?]解析并加入[直连列表] GeoIP 到 NFTSET 完成" echolog " - [$?]解析并加入[直连列表] GeoIP 到 NFTSET 完成"
fi fi
} }
@@ -1038,13 +1050,13 @@ add_firewall_rule() {
#代理列表 #代理列表
[ "$USE_PROXY_LIST_ALL" = "1" ] && { [ "$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 "$IPv4_REGEX" | 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 "$IPv6_REGEX" | insert_nftset $NFTSET_BLACK6_STATIC
[ "$USE_GEOVIEW" = "1" ] && { [ "$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') 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 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 ipv4 | 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 ipv6 | insert_nftset $NFTSET_BLACK6_STATIC
echolog " - [$?]解析并加入[代理列表] GeoIP 到 NFTSET 完成" echolog " - [$?]解析并加入[代理列表] GeoIP 到 NFTSET 完成"
fi fi
} }
@@ -1052,13 +1064,13 @@ add_firewall_rule() {
#屏蔽列表 #屏蔽列表
[ "$USE_BLOCK_LIST_ALL" = "1" ] && { [ "$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 "$IPv4_REGEX" | 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 "$IPv6_REGEX" | insert_nftset $NFTSET_BLOCK6_STATIC
[ "$USE_GEOVIEW" = "1" ] && { [ "$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') 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 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 ipv4 | 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 ipv6 | insert_nftset $NFTSET_BLOCK6_STATIC
echolog " - [$?]解析并加入[屏蔽列表] GeoIP 到 NFTSET 完成" echolog " - [$?]解析并加入[屏蔽列表] GeoIP 到 NFTSET 完成"
fi fi
} }
@@ -1072,16 +1084,16 @@ add_firewall_rule() {
local shunt_id local shunt_id
for shunt_id in $shunt_ids; do for shunt_id in $shunt_ids; do
[ "${shunt_group}" != "$(config_n_get ${shunt_id} group)" ] && continue [ "${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 "$IPv4_REGEX" | 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 "$IPv6_REGEX" | insert_nftset $NFTSET_SHUNT6_STATIC
[ "$USE_GEOVIEW" = "1" ] && { [ "$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') 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" [ -n "$geoip_code" ] && GEOIP_CODE="${GEOIP_CODE:+$GEOIP_CODE,}$geoip_code"
} }
done done
if [ -n "$GEOIP_CODE" ]; then 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 ipv4 | 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 ipv6 | insert_nftset $NFTSET_SHUNT6_STATIC
echolog " - [$?]解析并加入[分流节点] GeoIP 到 NFTSET 完成" echolog " - [$?]解析并加入[分流节点] GeoIP 到 NFTSET 完成"
fi fi
} }
@@ -1296,8 +1308,6 @@ add_firewall_rule() {
if [ -n "$NODE" ] && ([ -n "${LOCALHOST_TCP_PROXY_MODE}" ] || [ -n "${LOCALHOST_UDP_PROXY_MODE}" ]); then if [ -n "$NODE" ] && ([ -n "${LOCALHOST_TCP_PROXY_MODE}" ] || [ -n "${LOCALHOST_UDP_PROXY_MODE}" ]); then
[ -n "$DNS_REDIRECT_PORT" ] && { [ -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 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\"" nft "add rule $NFTABLE_NAME nat_output meta l4proto tcp oif lo tcp dport 53 counter redirect to :$DNS_REDIRECT_PORT comment \"PSW_DNS\""
} }
@@ -183,8 +183,6 @@ test_auto_switch() {
start() { start() {
id=$1 id=$1
LOCK_FILE=${LOCK_PATH}/${CONFIG}_socks_auto_switch_${id}.lock 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) main_node=$(config_n_get $id node)
socks_port=$(config_n_get $id port 0) socks_port=$(config_n_get $id port 0)
delay=$(config_n_get $id autoswitch_testing_time 30) delay=$(config_n_get $id autoswitch_testing_time 30)
@@ -641,21 +641,6 @@ local function processData(szType, content, add_mode, group, sub_cfg)
if info.net == 'ws' then if info.net == 'ws' then
result.ws_host = info.host result.ws_host = info.host
result.ws_path = info.path result.ws_path = info.path
if result.type == "sing-box" and info.path then
local ws_path_dat = split(info.path, "?")
local ws_path = ws_path_dat[1]
local ws_path_params = {}
for _, v in pairs(split(ws_path_dat[2], '&')) do
local t = split(v, '=')
ws_path_params[t[1]] = t[2]
end
if ws_path_params.ed and tonumber(ws_path_params.ed) then
result.ws_path = ws_path
result.ws_enableEarlyData = "1"
result.ws_maxEarlyData = tonumber(ws_path_params.ed)
result.ws_earlyDataHeaderName = "Sec-WebSocket-Protocol"
end
end
end end
if info.net == "http" then if info.net == "http" then
if result.type == "Xray" then if result.type == "Xray" then
@@ -897,21 +882,6 @@ local function processData(szType, content, add_mode, group, sub_cfg)
if params.type == 'ws' then if params.type == 'ws' then
result.ws_host = params.host result.ws_host = params.host
result.ws_path = params.path result.ws_path = params.path
if result.type == "sing-box" and params.path then
local ws_path_dat = split(params.path, "%?")
local ws_path = ws_path_dat[1]
local ws_path_params = {}
for _, v in pairs(split(ws_path_dat[2], '&')) do
local t = split(v, '=')
ws_path_params[t[1]] = t[2]
end
if ws_path_params.ed and tonumber(ws_path_params.ed) then
result.ws_path = ws_path
result.ws_enableEarlyData = "1"
result.ws_maxEarlyData = tonumber(ws_path_params.ed)
result.ws_earlyDataHeaderName = "Sec-WebSocket-Protocol"
end
end
end end
if params.type == "http" then if params.type == "http" then
if result.type == "sing-box" then if result.type == "sing-box" then
@@ -1124,21 +1094,6 @@ local function processData(szType, content, add_mode, group, sub_cfg)
if params.type == 'ws' then if params.type == 'ws' then
result.ws_host = params.host result.ws_host = params.host
result.ws_path = params.path result.ws_path = params.path
if result.type == "sing-box" and params.path then
local ws_path_dat = split(params.path, "%?")
local ws_path = ws_path_dat[1]
local ws_path_params = {}
for _, v in pairs(split(ws_path_dat[2], '&')) do
local t = split(v, '=')
ws_path_params[t[1]] = t[2]
end
if ws_path_params.ed and tonumber(ws_path_params.ed) then
result.ws_path = ws_path
result.ws_enableEarlyData = "1"
result.ws_maxEarlyData = tonumber(ws_path_params.ed)
result.ws_earlyDataHeaderName = "Sec-WebSocket-Protocol"
end
end
end end
if params.type == "http" then if params.type == "http" then
if result.type == "sing-box" then if result.type == "sing-box" then
@@ -1266,21 +1221,6 @@ local function processData(szType, content, add_mode, group, sub_cfg)
if params.type == 'ws' then if params.type == 'ws' then
result.ws_host = params.host result.ws_host = params.host
result.ws_path = params.path result.ws_path = params.path
if result.type == "sing-box" and params.path then
local ws_path_dat = split(params.path, "%?")
local ws_path = ws_path_dat[1]
local ws_path_params = {}
for _, v in pairs(split(ws_path_dat[2], '&')) do
local t = split(v, '=')
ws_path_params[t[1]] = t[2]
end
if ws_path_params.ed and tonumber(ws_path_params.ed) then
result.ws_path = ws_path
result.ws_enableEarlyData = "1"
result.ws_maxEarlyData = tonumber(ws_path_params.ed)
result.ws_earlyDataHeaderName = "Sec-WebSocket-Protocol"
end
end
end end
if params.type == "http" then if params.type == "http" then
if result.type == "sing-box" then if result.type == "sing-box" then
@@ -40,10 +40,11 @@ do
mkdir -p $TMP_SUB_PATH mkdir -p $TMP_SUB_PATH
for item in $(uci show ${CONFIG} | grep "=subscribe_list" | cut -d '.' -sf 2 | cut -d '=' -sf 1); do 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) sub_update_week_mode=$(config_n_get $item update_week_mode)
if [ -n "$sub_update_week_mode" ]; then if [ -n "$sub_update_week_mode" ] && [ "$sub_update_week_mode" = "8" ]; then
remark=$(config_n_get $item remark)
sub_update_interval_mode=$(config_n_get $item update_interval_mode) sub_update_interval_mode=$(config_n_get $item update_interval_mode)
[ -n "$sub_update_interval_mode" ] && {
echo "$item" >> $TMP_SUB_PATH/${sub_update_week_mode}_${sub_update_interval_mode} echo "$item" >> $TMP_SUB_PATH/${sub_update_week_mode}_${sub_update_interval_mode}
}
fi fi
done done
@@ -56,7 +57,6 @@ do
[ "$sub_update_week_mode" = "8" ] && { [ "$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 & } [ "$(expr "$CFG_UPDATE_INT" % "$sub_update_interval_mode")" -eq 0 ] && { lua $APP_PATH/subscribe.lua start $cfgids cron > /dev/null 2>&1 & }
} }
done done
rm -rf $TMP_SUB_PATH rm -rf $TMP_SUB_PATH
} }

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