🌈 Sync 2026-05-19 02:35:14

This commit is contained in:
github-actions[bot] committed 2026-05-19 02:35:14 +08:00
1 parent 595807ea37
commit ca5e918f4b
81 files changed
+12000 -3190

No files matched your search

-131
View File
@@ -1,131 +0,0 @@
#
# Copyright (C) 2010-2016 OpenWrt.org
# Copyright (C) 2009-2016 Thomas Heil <heil@terminal-consulting.de>
# Copyright (C) 2018 Christian Lachner <gladiac@gmail.com>
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=haproxy
PKG_VERSION:=3.2.19
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://www.haproxy.org/download/3.2/src
PKG_HASH:=skip
PKG_MAINTAINER:=Thomas Heil <heil@terminal-consulting.de>, \
Christian Lachner <gladiac@gmail.com>
PKG_LICENSE:=GPL-2.0-only
PKG_LICENSE_FILES:=LICENSE
PKG_CPE_ID:=cpe:/a:haproxy:haproxy
include $(INCLUDE_DIR)/package.mk
define Package/haproxy/Default
SUBMENU:=Web Servers/Proxies
SECTION:=net
CATEGORY:=Network
TITLE:=TCP/HTTP Load Balancer
URL:=https://www.haproxy.org/
DEPENDS:= +USE_GLIBC:libcrypt-compat +libpcre2 +libltdl +zlib +libpthread +liblua5.4 +libatomic
endef
define Package/haproxy/Default/description
Open source Reliable, High Performance TCP/HTTP Load Balancer.
endef
define Package/haproxy
$(call Package/haproxy/Default)
TITLE+=with SSL support
DEPENDS+= +libopenssl +libncurses +libreadline
VARIANT:=ssl
endef
define Package/haproxy/description
$(call Package/haproxy/Default/description)
This package is built with SSL and LUA support.
endef
define Package/haproxy-nossl
$(call Package/haproxy/Default)
TITLE+=without SSL support
VARIANT:=nossl
CONFLICTS:=haproxy
endef
define Package/haproxy-nossl/description
$(call Package/haproxy/Default/description)
This package is built without SSL support.
endef
TARGET=linux-musl
ENABLE_LUA:=y
ifeq ($(CONFIG_USE_GLIBC),y)
TARGET=linux-glibc
endif
ifeq ($(BUILD_VARIANT),ssl)
ADDON+=USE_OPENSSL=1
ADDON+=ADDLIB="-lcrypto -lm"
ADDON+=USE_QUIC=1
endif
define Build/Compile
$(MAKE) TARGET=$(TARGET) -C $(PKG_BUILD_DIR) \
DESTDIR="$(PKG_INSTALL_DIR)" \
CC="$(TARGET_CC)" \
PCREDIR="$(STAGING_DIR)/usr/" \
USE_LUA=1 LUA_LIB_NAME="lua5.4" LUA_INC="$(STAGING_DIR)/usr/include/lua5.4" LUA_LIB="$(STAGING_DIR)/usr/lib" \
USE_ZLIB=1 USE_PCRE2=1 USE_PCRE2_JIT=1 USE_LIBATOMIC=1 USE_PROMEX=1 \
VERSION="$(PKG_VERSION)" SUBVERS="-$(PKG_RELEASE)" \
VERDATE="$(shell date -d @$(SOURCE_DATE_EPOCH) '+%Y/%m/%d')" IGNOREGIT=1 \
$(ADDON) \
CFLAGS="$(TARGET_CFLAGS) -fno-strict-aliasing -Wdeclaration-after-statement -Wno-unused-label -Wno-sign-compare -Wno-unused-parameter -Wno-clobbered -Wno-missing-field-initializers -Wno-cast-function-type -Wno-address-of-packed-member -Wtype-limits -Wshift-negative-value -Wshift-overflow=2 -Wduplicated-cond -Wnull-dereference -fwrapv -fasynchronous-unwind-tables -Wno-null-dereference" \
LD="$(TARGET_CC)" \
LDFLAGS="$(TARGET_LDFLAGS)"
$(MAKE_VARS) $(MAKE) -C $(PKG_BUILD_DIR) \
DESTDIR="$(PKG_INSTALL_DIR)" \
LD="$(TARGET_CC)" \
LDFLAGS="$(TARGET_LDFLAGS)" \
$(MAKE_FLAGS) \
install
$(MAKE_VARS) $(MAKE) -C $(PKG_BUILD_DIR) \
DESTDIR="$(PKG_INSTALL_DIR)" \
CC="$(TARGET_CC)" \
CFLAGS="$(TARGET_CFLAGS) -Wno-address-of-packed-member" \
LDFLAGS="$(TARGET_LDFLAGS)" \
admin/halog/halog
endef
define Package/haproxy/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/haproxy $(1)/usr/sbin/
endef
Package/haproxy-nossl/install = $(Package/haproxy/install)
define Package/halog
$(call Package/haproxy)
TITLE+=halog
DEPENDS:=haproxy
endef
define Package/halog/description
HAProxy Log Analyzer
endef
define Package/halog/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/admin/halog/halog $(1)/usr/bin/
endef
$(eval $(call BuildPackage,haproxy))
$(eval $(call BuildPackage,halog))
$(eval $(call BuildPackage,haproxy-nossl))
+2 -2
View File
@@ -16,8 +16,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-amlogic PKG_NAME:=luci-app-amlogic
PKG_VERSION:=3.1.313 PKG_VERSION:=3.1.315
PKG_RELEASE:=11 PKG_RELEASE:=12
PKG_LICENSE:=GPL-2.0 License PKG_LICENSE:=GPL-2.0 License
PKG_MAINTAINER:=ophub <https://github.com/ophub/luci-app-amlogic> PKG_MAINTAINER:=ophub <https://github.com/ophub/luci-app-amlogic>
@@ -162,6 +162,7 @@ const inbound_type = [
['anytls', _('AnyTLS') + ' - ' + _('TCP')], ['anytls', _('AnyTLS') + ' - ' + _('TCP')],
['tuic', _('TUIC') + ' - ' + _('UDP')], ['tuic', _('TUIC') + ' - ' + _('UDP')],
['hysteria2', _('Hysteria2') + ' - ' + _('UDP')], ['hysteria2', _('Hysteria2') + ' - ' + _('UDP')],
['hysteria2-realm', _('Hysteria2 Realm') + ' - ' + _('TCP/UDP')],
['trusttunnel', _('TrustTunnel') + ' - ' + _('TCP/UDP')], ['trusttunnel', _('TrustTunnel') + ' - ' + _('TCP/UDP')],
['tunnel', _('Tunnel') + ' - ' + _('TCP/UDP')] ['tunnel', _('Tunnel') + ' - ' + _('TCP/UDP')]
]; ];
@@ -118,6 +118,7 @@ function renderListeners(s, uciconfig, isClient) {
s.tab('field_general', _('General fields')); s.tab('field_general', _('General fields'));
s.tab('field_vless_encryption', _('Vless Encryption fields')); s.tab('field_vless_encryption', _('Vless Encryption fields'));
s.tab('field_hysteria2_realm', _('Hysteria2 Realm fields'));
s.tab('field_tls', _('TLS fields')); s.tab('field_tls', _('TLS fields'));
s.tab('field_transport', _('Transport fields')); s.tab('field_transport', _('Transport fields'));
s.tab('field_multiplex', _('Multiplex fields')); s.tab('field_multiplex', _('Multiplex fields'));
@@ -209,6 +210,36 @@ function renderListeners(s, uciconfig, isClient) {
o.depends('type', 'hysteria2'); o.depends('type', 'hysteria2');
o.modalonly = true; o.modalonly = true;
/* Hysteria2 Realmserver fields */
o = s.taboption('field_general', form.Value, 'hysteria2_realmserver_token', _('Pre-shared key'));
o.placeholder = 'public';
o.rmempty = false;
o.depends('type', 'hysteria2-realm');
o.modalonly = true;
o = s.taboption('field_general', form.Value, 'hysteria2_realmserver_max_realms', _('Max realms'));
o.datatype = 'uinteger';
o.placeholder = '65536';
o.depends('type', 'hysteria2-realm');
o.modalonly = true;
o = s.taboption('field_general', form.Value, 'hysteria2_realmserver_max_realms_per_ip', _('Max realms per client IP'));
o.datatype = 'uinteger';
o.placeholder = '4';
o.depends('type', 'hysteria2-realm');
o.modalonly = true;
o = s.taboption('field_general', form.Value, 'hysteria2_realmserver_trusted_proxy_header', _('Trusted proxy header'),
_('Header to read real client IP from (e.g. X-Forwarded-For)'));
o.depends('type', 'hysteria2-realm');
o.modalonly = true;
o = s.taboption('field_general', form.Value, 'hysteria2_realmserver_realm_name_pattern', _('Realm name pattern'));
o.default = '^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$';
o.rmempty = false;
o.depends('type', 'hysteria2-realm');
o.modalonly = true;
/* Shadowsocks fields */ /* Shadowsocks fields */
o = s.taboption('field_general', form.ListValue, 'shadowsocks_chipher', _('Chipher')); o = s.taboption('field_general', form.ListValue, 'shadowsocks_chipher', _('Chipher'));
o.default = hm.shadowsocks_cipher_methods[1][0]; o.default = hm.shadowsocks_cipher_methods[1][0];
@@ -498,11 +529,18 @@ function renderListeners(s, uciconfig, isClient) {
/* Plugin fields */ /* Plugin fields */
o = s.taboption('field_general', form.ListValue, 'plugin', _('Plugin')); o = s.taboption('field_general', form.ListValue, 'plugin', _('Plugin'));
o.value('', _('none')); o.value('', _('none'));
o.value('obfs', _('obfs-simple'));
o.value('shadow-tls', _('shadow-tls')); o.value('shadow-tls', _('shadow-tls'));
//o.value('kcp-tun', _('kcp-tun')); //o.value('kcp-tun', _('kcp-tun'));
o.depends('type', 'shadowsocks'); o.depends('type', 'shadowsocks');
o.modalonly = true; o.modalonly = true;
o = s.taboption('field_general', form.ListValue, 'plugin_opts_obfsmode', _('Plugin: ') + _('Obfs Mode'));
o.value('http', _('HTTP'));
o.value('tls', _('TLS'));
o.depends('plugin', 'obfs');
o.modalonly = true;
o = s.taboption('field_general', form.Value, 'plugin_opts_handshake_dest', _('Plugin: ') + _('Handshake target that supports TLS 1.3')); o = s.taboption('field_general', form.Value, 'plugin_opts_handshake_dest', _('Plugin: ') + _('Handshake target that supports TLS 1.3'));
o.datatype = 'hostport'; o.datatype = 'hostport';
o.placeholder = 'cloud.tencent.com:443'; o.placeholder = 'cloud.tencent.com:443';
@@ -782,6 +820,40 @@ function renderListeners(s, uciconfig, isClient) {
o.depends('vless_decryption', '1'); o.depends('vless_decryption', '1');
o.modalonly = true; o.modalonly = true;
// @xhttp-config其他的配置
/* Hysteria2 Realm fields */
o = s.taboption('field_general', form.Flag, 'hysteria2_realm', _('Realm'));
o.default = o.disabled;
o.depends('type', 'hysteria2');
o.modalonly = true;
o = s.taboption('field_hysteria2_realm', form.Value, 'hysteria2_realm_server_url', _('Rendezvous server'));
o.placeholder = 'https://realm.hy2.io';
o.rmempty = false;
o.depends('hysteria2_realm', '1');
o.modalonly = true;
o = s.taboption('field_hysteria2_realm', form.Value, 'hysteria2_realm_token', _('Pre-shared key of rendezvous server'));
o.placeholder = 'public';
o.depends('hysteria2_realm', '1');
o.modalonly = true;
o = s.taboption('field_hysteria2_realm', form.Value, 'hysteria2_realm_id', _('Realm ID'));
o.placeholder = 'my-cabin-1f3a8c2e9b';
o.rmempty = false;
o.depends('hysteria2_realm', '1');
o.modalonly = true;
o = s.taboption('field_hysteria2_realm', form.DynamicList, 'hysteria2_realm_stun_servers', _('STUN servers'));
o.datatype = 'hostport';
o.default = ['stun.nextcloud.com:3478','stun.sip.us:3478','global.stun.twilio.com:3478'];
o.rmempty = false;
o.depends('hysteria2_realm', '1');
o.modalonly = true;
// @ 下面支持填写针对server-url的TLS配置(sni, skip-cert-verify, fingerprint, certificate, private-key, alpn)
/* TLS fields */ /* TLS fields */
o = s.taboption('field_general', form.Flag, 'tls', _('TLS')); o = s.taboption('field_general', form.Flag, 'tls', _('TLS'));
o.default = o.disabled; o.default = o.disabled;
@@ -795,12 +867,29 @@ function renderListeners(s, uciconfig, isClient) {
if (['trojan', 'anytls', 'tuic', 'hysteria2', 'trusttunnel'].includes(type)) { if (['trojan', 'anytls', 'tuic', 'hysteria2', 'trusttunnel'].includes(type)) {
tls.checked = true; tls.checked = true;
tls.disabled = true; tls.disabled = true;
if (['tuic', 'hysteria2'].includes(type) && !`${tls_alpn.getValue()}`)
tls_alpn.setValue('h3');
} else { } else {
tls.removeAttribute('disabled'); tls.removeAttribute('disabled');
} }
// Default alpn
if (!`${tls_alpn.getValue()}`) {
let def_alpn;
switch (type) {
case 'hysteria2':
case 'tuic':
def_alpn = ['h3'];
break;
case 'hysteria2-realm':
def_alpn = ['h2', 'http/1.1'];
break;
default:
def_alpn = [];
}
tls_alpn.setValue(def_alpn);
}
// Force disabled // Force disabled
if (['trusttunnel'].includes(type)) { if (['trusttunnel'].includes(type)) {
tls_alpn.node.querySelector('input').disabled = true; tls_alpn.node.querySelector('input').disabled = true;
@@ -817,7 +906,7 @@ function renderListeners(s, uciconfig, isClient) {
return true; return true;
} }
o.depends({type: /^(http|socks|mixed|vmess|vless|trojan|anytls|tuic|hysteria2|trusttunnel)$/}); o.depends({type: /^(http|socks|mixed|vmess|vless|trojan|anytls|tuic|hysteria2|hysteria2-realm|trusttunnel)$/});
o.modalonly = true; o.modalonly = true;
o = s.taboption('field_tls', form.DynamicList, 'tls_alpn', _('TLS ALPN'), o = s.taboption('field_tls', form.DynamicList, 'tls_alpn', _('TLS ALPN'),
@@ -860,7 +949,7 @@ function renderListeners(s, uciconfig, isClient) {
hm.tls_client_auth_types.forEach((res) => { hm.tls_client_auth_types.forEach((res) => {
o.value.apply(o, res); o.value.apply(o, res);
}) })
o.depends({tls: '1', type: /^(http|socks|mixed|vmess|vless|trojan|anytls|hysteria2|tuic|trusttunnel)$/}); o.depends({tls: '1', type: /^(http|socks|mixed|vmess|vless|trojan|anytls|hysteria2|hysteria2-realm|tuic|trusttunnel)$/});
o.modalonly = true; o.modalonly = true;
o = s.taboption('field_tls', form.Value, 'tls_client_auth_cert_path', _('Client Auth Certificate path') + _(' (mTLS)'), o = s.taboption('field_tls', form.Value, 'tls_client_auth_cert_path', _('Client Auth Certificate path') + _(' (mTLS)'),
@@ -869,7 +958,7 @@ function renderListeners(s, uciconfig, isClient) {
o.validate = function(/* ... */) { o.validate = function(/* ... */) {
return hm.validateMTLSClientAuth.call(this, 'tls_client_auth_type', ...arguments); return hm.validateMTLSClientAuth.call(this, 'tls_client_auth_type', ...arguments);
} }
o.depends({tls: '1', type: /^(http|socks|mixed|vmess|vless|trojan|anytls|hysteria2|tuic|trusttunnel)$/}); o.depends({tls: '1', type: /^(http|socks|mixed|vmess|vless|trojan|anytls|hysteria2|hysteria2-realm|tuic|trusttunnel)$/});
o.modalonly = true; o.modalonly = true;
o = s.taboption('field_tls', form.Button, '_upload_client_auth_cert', _('Upload certificate') + _(' (mTLS)'), o = s.taboption('field_tls', form.Button, '_upload_client_auth_cert', _('Upload certificate') + _(' (mTLS)'),
@@ -918,13 +1007,13 @@ function renderListeners(s, uciconfig, isClient) {
return node; return node;
} }
o.depends({tls: '1', type: /^(http|socks|mixed|vmess|vless|trojan|anytls|hysteria2|tuic|trusttunnel)$/}); o.depends({tls: '1', type: /^(http|socks|mixed|vmess|vless|trojan|anytls|hysteria2|hysteria2-realm|tuic|trusttunnel)$/});
o.modalonly = true; o.modalonly = true;
o = s.taboption('field_tls', hm.CopyValue, 'tls_ech_config', _('ECH config'), o = s.taboption('field_tls', hm.CopyValue, 'tls_ech_config', _('ECH config'),
_('This ECH parameter needs to be added to the HTTPS record of the domain.')); _('This ECH parameter needs to be added to the HTTPS record of the domain.'));
o.placeholder = 'AEn+DQBFKwAgACABWIHUGj4u+PIggYXcR5JF0gYk3dCRioBW8uJq9H4mKAAIAAEAAQABAANAEnB1YmxpYy50bHMtZWNoLmRldgAA'; o.placeholder = 'AEn+DQBFKwAgACABWIHUGj4u+PIggYXcR5JF0gYk3dCRioBW8uJq9H4mKAAIAAEAAQABAANAEnB1YmxpYy50bHMtZWNoLmRldgAA';
o.depends({tls: '1', type: /^(http|socks|mixed|vmess|vless|trojan|anytls|hysteria2|tuic|trusttunnel)$/}); o.depends({tls: '1', type: /^(http|socks|mixed|vmess|vless|trojan|anytls|hysteria2|hysteria2-realm|tuic|trusttunnel)$/});
o.modalonly = true; o.modalonly = true;
// uTLS fields // uTLS fields
@@ -221,6 +221,7 @@ return view.extend({
ss.tab('field_general', _('General fields')); ss.tab('field_general', _('General fields'));
ss.tab('field_vless_encryption', _('Vless Encryption fields')); ss.tab('field_vless_encryption', _('Vless Encryption fields'));
ss.tab('field_hysteria2_realm', _('Hysteria2 Realm fields'));
ss.tab('field_tls', _('TLS fields')); ss.tab('field_tls', _('TLS fields'));
ss.tab('field_transport', _('Transport fields')); ss.tab('field_transport', _('Transport fields'));
ss.tab('field_multiplex', _('Multiplex fields')); ss.tab('field_multiplex', _('Multiplex fields'));
@@ -956,6 +957,40 @@ return view.extend({
so.depends('vless_encryption', '1'); so.depends('vless_encryption', '1');
so.modalonly = true; so.modalonly = true;
// @ 下面支持填写针对download-settings的上下行分离配置
/* Hysteria2 Realm fields */
so = ss.taboption('field_general', form.Flag, 'hysteria2_realm', _('Realm'));
so.default = so.disabled;
so.depends('type', 'hysteria2');
so.modalonly = true;
so = ss.taboption('field_hysteria2_realm', form.Value, 'hysteria2_realm_server_url', _('Rendezvous server'));
so.placeholder = 'https://realm.hy2.io';
so.rmempty = false;
so.depends('hysteria2_realm', '1');
so.modalonly = true;
so = ss.taboption('field_hysteria2_realm', form.Value, 'hysteria2_realm_token', _('Pre-shared key of rendezvous server'));
so.placeholder = 'public';
so.depends('hysteria2_realm', '1');
so.modalonly = true;
so = ss.taboption('field_hysteria2_realm', form.Value, 'hysteria2_realm_id', _('Realm ID'));
so.placeholder = 'my-cabin-1f3a8c2e9b';
so.rmempty = false;
so.depends('hysteria2_realm', '1');
so.modalonly = true;
so = ss.taboption('field_hysteria2_realm', form.DynamicList, 'hysteria2_realm_stun_servers', _('STUN servers'));
so.datatype = 'hostport';
so.default = ['stun.nextcloud.com:3478','stun.sip.us:3478','global.stun.twilio.com:3478'];
so.rmempty = false;
so.depends('hysteria2_realm', '1');
so.modalonly = true;
// @ 下面支持填写针对server-url的TLS配置(sni, skip-cert-verify, fingerprint, certificate, private-key, alpn)
/* TLS fields */ /* TLS fields */
so = ss.taboption('field_general', form.Flag, 'tls', _('TLS')); so = ss.taboption('field_general', form.Flag, 'tls', _('TLS'));
so.default = so.disabled; so.default = so.disabled;
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -511,6 +511,20 @@ uci.foreach(uciconf, ucinode, (cfg) => {
down: cfg.hysteria_down_mbps ? cfg.hysteria_down_mbps + ' Mbps' : null, down: cfg.hysteria_down_mbps ? cfg.hysteria_down_mbps + ' Mbps' : null,
obfs: cfg.hysteria_obfs_type, obfs: cfg.hysteria_obfs_type,
"obfs-password": cfg.hysteria_obfs_password, "obfs-password": cfg.hysteria_obfs_password,
"realm-opts": cfg.hysteria2_realm === '1' ? {
enable: true,
"server-url": cfg.hysteria2_realm_server_url,
token: cfg.hysteria2_realm_token,
"realm-id": cfg.hysteria2_realm_id,
"stun-servers": cfg.hysteria2_realm_stun_servers,
// @TLS of server-url
//sni,
//alpn,
//"skip-cert-verify",
//fingerprint,
//certificate,
//"private-key"
} : null,
/* SSH */ /* SSH */
"private-key-passphrase": cfg.ssh_priv_key_passphrase, "private-key-passphrase": cfg.ssh_priv_key_passphrase,
@@ -680,6 +694,7 @@ uci.foreach(uciconf, ucinode, (cfg) => {
"h-max-request-times": cfg.transport_xhttp_xmux_max_request_times, "h-max-request-times": cfg.transport_xhttp_xmux_max_request_times,
"h-max-reusable-secs": cfg.transport_xhttp_xmux_max_reusable_secs, "h-max-reusable-secs": cfg.transport_xhttp_xmux_max_reusable_secs,
"h-keep-alive-period": strToInt(cfg.transport_xhttp_xmux_keep_alive_period) "h-keep-alive-period": strToInt(cfg.transport_xhttp_xmux_keep_alive_period)
// @download-settings
} : null } : null
} : null } : null
} : {}), } : {}),
@@ -251,6 +251,27 @@ export function parseListener(cfg, isClient, label) {
obfs: cfg.hysteria_obfs_type, obfs: cfg.hysteria_obfs_type,
"obfs-password": cfg.hysteria_obfs_password, "obfs-password": cfg.hysteria_obfs_password,
masquerade: cfg.hysteria_masquerade, masquerade: cfg.hysteria_masquerade,
"realm-opts": cfg.hysteria2_realm === '1' ? {
enable: true,
"server-url": cfg.hysteria2_realm_server_url,
token: cfg.hysteria2_realm_token,
"realm-id": cfg.hysteria2_realm_id,
"stun-servers": cfg.hysteria2_realm_stun_servers,
// @TLS of server-url
//sni,
//alpn,
//"skip-cert-verify",
//fingerprint,
//certificate,
//"private-key"
} : null,
/* Hysteria2 Realmserver */
token: cfg.hysteria2_realmserver_token,
"max-realms": strToInt(cfg.hysteria2_realmserver_max_realms),
"max-realms-per-ip": strToInt(cfg.hysteria2_realmserver_max_realms_per_ip),
"trusted-proxy-header": cfg.hysteria2_realmserver_trusted_proxy_header,
"realm-name-pattern": cfg.hysteria2_realmserver_realm_name_pattern,
/* Shadowsocks */ /* Shadowsocks */
cipher: cfg.shadowsocks_chipher, cipher: cfg.shadowsocks_chipher,
@@ -302,6 +323,11 @@ export function parseListener(cfg, isClient, label) {
/* Plugin fields */ /* Plugin fields */
...(cfg.plugin ? { ...(cfg.plugin ? {
// obfs-simple
"simple-obfs": cfg.plugin === 'obfs' ? {
enable: true,
mode: cfg.plugin_opts_obfsmode
} : null,
// shadow-tls // shadow-tls
"shadow-tls": cfg.plugin === 'shadow-tls' ? { "shadow-tls": cfg.plugin === 'shadow-tls' ? {
enable: true, enable: true,
@@ -357,6 +383,7 @@ export function parseListener(cfg, isClient, label) {
"sc-max-buffered-posts": strToInt(cfg.transport_xhttp_sc_max_buffered_posts) || null, "sc-max-buffered-posts": strToInt(cfg.transport_xhttp_sc_max_buffered_posts) || null,
"sc-stream-up-server-secs": cfg.transport_xhttp_sc_stream_up_server_secs, "sc-stream-up-server-secs": cfg.transport_xhttp_sc_stream_up_server_secs,
"sc-max-each-post-bytes": strToInt(cfg.transport_xhttp_sc_max_each_post_bytes) || null, "sc-max-each-post-bytes": strToInt(cfg.transport_xhttp_sc_max_each_post_bytes) || null,
// @其他的配置
} : null } : null
} : {}) } : {})
} }
+2 -2
View File
@@ -3,8 +3,8 @@ include $(TOPDIR)/rules.mk
LUCI_TITLE:=Information dashboard for 3G/LTE dongle LUCI_TITLE:=Information dashboard for 3G/LTE dongle
LUCI_DEPENDS:=+modeminfo LUCI_DEPENDS:=+modeminfo
PKG_LICENSE:=GPLv3 PKG_LICENSE:=GPLv3
PKG_VERSION:=0.4.6 PKG_VERSION:=0.4.7
PKG_RELEASE:=2 PKG_RELEASE:=3
include $(TOPDIR)/feeds/luci/luci.mk include $(TOPDIR)/feeds/luci/luci.mk
@@ -0,0 +1,115 @@
'use strict';
'require ui';
'require poll';
'require fs';
'require uci';
function getSignalColor(percent) {
if (percent === null || percent === undefined || percent < 5) {
return '#000000';
}
let p = percent;
if (p >= 80) return '#00cc00';
if (p >= 67) {
const ratio = (p - 50) / 30;
const r = Math.floor(255 * (1 - ratio));
const g = 255;
return `rgb(${r}, ${g}, 0)`;
}
if (p >= 35) {
const ratio = (p - 25) / 25;
const r = 255;
const g = Math.floor(165 + (90 * ratio));
return `rgb(${r}, ${g}, 0)`;
}
const ratio = (p - 5) / 20;
const r = 255;
const g = Math.floor(165 * ratio);
return `rgb(${r}, ${g}, 0)`;
}
function getSignalState(percent) {
const p = percent || 0;
if (p < 10) return 'error';
if (p < 25) return 'warning';
if (p < 50) return 'warning';
return 'active';
}
function isUciIndexTwo() {
return L.resolveDefault(fs.exec_direct('/sbin/uci', ['get', 'modeminfo.@general[0].index']), '')
.then(function(value) {
return value.trim() === '2';
})
.catch(function() {
return false;
});
}
function updateIndicator() {
isUciIndexTwo().then(function(ok) {
if (!ok) {
ui.hideIndicator('modem-status');
return;
}
if (window.location.pathname.includes('/cgi-bin/luci/admin/modem/main')) {
ui.hideIndicator('modem-status');
return;
}
L.resolveDefault(fs.exec_direct('/usr/bin/modeminfo'), '{"modem":[]}')
.then(function(res) {
var data = JSON.parse(res);
if (!data.modem || !data.modem.length) {
ui.hideIndicator('modem-status');
return;
}
var parts = data.modem.map(function(modem) {
var percent = parseInt(modem.csq_per);
var cops = modem.cops || '';
var mode = modem.mode + (parseInt(modem.lteca) > 0 ? '+' : '');
return { mode: mode, cops: cops, percent: percent };
});
var minPercent = Math.min.apply(null, parts.map(function(p) { return p.percent; }));
var state = getSignalState(minPercent);
var status = parts.map(function(p) {
return p.mode + ' ' + p.cops;
}).join(' | ');
ui.showIndicator('modem-status', status, null, state);
var indicator = document.querySelector('[data-indicator="modem-status"]');
if (indicator) {
var html = parts.map(function(p) {
var color = getSignalColor(p.percent);
return '<span style="color:' + color + '; -webkit-text-stroke: 0.5px black; text-stroke: 0.5px black;">●</span> ' + p.mode + ' ' + p.cops;
}).join(' | ');
indicator.innerHTML = html;
}
})
.catch(function() {
ui.hideIndicator('modem-status');
});
});
}
setTimeout(function() {
updateIndicator();
poll.add(updateIndicator, 10);
}, 500);
return L.Class.extend({
__name__: 'ModemIndicator'
});
@@ -0,0 +1,6 @@
'use strict';
'require modem-indicator';
return L.Class.extend({
__name__: 'preload.modem-indicator'
});
@@ -60,7 +60,7 @@ var qdesc = _('Select qmi port.');
var lacdec = _('Show LAC and CID in decimal.'); var lacdec = _('Show LAC and CID in decimal.');
var mmdesc = _('Get device hardware name via mmcli utility if aviable.'); var mmdesc = _('Get device hardware name via mmcli utility if aviable.');
var qmidesc = _('Set qmi mode.'); var qmidesc = _('Set qmi mode.');
var idesc = _('Short info on Overview page'); var idesc = _('Show short info.<br />Overview: show info on main page \"Cellular Network\" section<br />MenuBar: show info on menubar all pages<br />NOTICE: Don\'t add too many modems in menubar, it may break the theme display.');
var portplace = _('Please select a port'); var portplace = _('Please select a port');
return view.extend({ return view.extend({
@@ -88,8 +88,12 @@ return view.extend({
s = m.section(form.TypedSection, 'general', _('General option'), null); s = m.section(form.TypedSection, 'general', _('General option'), null);
s.anonymous = true; s.anonymous = true;
o = s.option(form.Flag, 'index', _('Index page'), idesc); o = s.option(form.ListValue, 'index', _('Short info'), idesc);
s.anonymous = true; o.widget="radio";
o.value(0,_('none'));
o.value(1,_('overview'));
o.value(2,_('menubar'));
o.default = '0';
o.rmempty = true; o.rmempty = true;
o = s.option(form.Flag, 'decimail', _('Show decimal'), lacdec); o = s.option(form.Flag, 'decimail', _('Show decimal'), lacdec);
@@ -19,7 +19,11 @@ return baseclass.extend({
title: _('Cellular network'), title: _('Cellular network'),
load: async function() { load: async function() {
uci.load('modeminfo'); await uci.load('modeminfo');
var index = uci.sections('modeminfo', 'general');
if (!index || !index[0] || index[0].index !== "1") {
return null;
};
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
@@ -43,10 +47,15 @@ return baseclass.extend({
render: function(data){ render: function(data){
if (!data || data.trim() === '') { if (!data || data.trim() === '') {
return E('div', { var index = uci.sections('modeminfo', 'general');
'class': 'cbi-section', if (!index || !index[0] || index[0].index !== "1") {
'style': 'color: #999; text-align: center;' return null;
}, _('Wait devices information')); } else {
return E('div', {
'class': 'cbi-section',
'style': 'color: #999; text-align: center;'
}, _('Wait devices information'));
}
} }
try { try {
+16 -4
View File
@@ -132,11 +132,23 @@ msgstr "Только QMI"
msgid "QMI over MBIM" msgid "QMI over MBIM"
msgstr "QMI через MBIM" msgstr "QMI через MBIM"
msgid "Index page" msgid "Short info"
msgstr "На главную" msgstr "Краткая информация"
msgid "Short info on Overview page" msgid "Show short info.<br />Overview: show info on main page \"Cellular Network\" section<br />MenuBar: show info on menubar all pages<br />NOTICE: Don\'t add too many modems in menubar, it may break the theme display."
msgstr "Краткая информация о сети на главной странице" msgstr "Показвать краткую информацию<br />На главной: показывать главной странице раздел \"Сотовая сеть\"<br />Меню: показывать в строке меню на всех страницах<br />ВНИМАНИЕ: не добавляйте слишком много устройств в строку меню, это может сломать отображение."
msgid "none"
msgstr "не отображать"
msgid "overview"
msgid "на главной"
msgid "menubar"
msgstr "в строке меню"
msgid "Wait devices information"
msgstr "Ожидаются данные устройств"
msgid "sec" msgid "sec"
msgstr "сек" msgstr "сек"
@@ -8,7 +8,8 @@
}, },
"file": { "file": {
"/usr/bin/modeminfo": [ "exec" ], "/usr/bin/modeminfo": [ "exec" ],
"/usr/share/modeminfo/scripts/rmtmp": [ "exec" ] "/usr/share/modeminfo/scripts/rmtmp": [ "exec" ],
"/sbin/uci": [ "exec" ]
}, },
"uci": [ "modeminfo" ] "uci": [ "modeminfo" ]
}, },
+1 -14
View File
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall PKG_NAME:=luci-app-passwall
PKG_VERSION:=26.5.11 PKG_VERSION:=26.5.11
PKG_RELEASE:=126 PKG_RELEASE:=127
PKG_PO_VERSION:=$(PKG_VERSION) PKG_PO_VERSION:=$(PKG_VERSION)
PKG_CONFIG_DEPENDS:= \ PKG_CONFIG_DEPENDS:= \
@@ -18,8 +18,6 @@ PKG_CONFIG_DEPENDS:= \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy \ CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria \ CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy \ CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Client \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Server \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Client \ CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Client \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Server \ CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Server \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client \ CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client \
@@ -43,9 +41,6 @@ LUCI_DEPENDS:=+coreutils +coreutils-base64 +coreutils-nohup +curl \
+PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy:haproxy \ +PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy:haproxy \
+PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria:hysteria \ +PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria:hysteria \
+PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy:naiveproxy \ +PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy:naiveproxy \
+PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Client:shadowsocks-libev-ss-local \
+PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Client:shadowsocks-libev-ss-redir \
+PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Server:shadowsocks-libev-ss-server \
+PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Client:shadowsocks-rust-sslocal \ +PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Client:shadowsocks-rust-sslocal \
+PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Server:shadowsocks-rust-ssserver \ +PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Server:shadowsocks-rust-ssserver \
+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client:shadowsocksr-libev-ssr-local \ +PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client:shadowsocksr-libev-ssr-local \
@@ -106,14 +101,6 @@ config PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy
select PACKAGE_naiveproxy select PACKAGE_naiveproxy
default n default n
config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Client
bool "Include Shadowsocks Libev Client"
default y
config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Server
bool "Include Shadowsocks Libev Server"
default y if aarch64||arm||i386||x86_64
config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Client config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Client
bool "Include Shadowsocks Rust Client" bool "Include Shadowsocks Rust Client"
depends on !i386 depends on !i386
@@ -1,7 +1,6 @@
local api = require "luci.passwall.api" local api = require "luci.passwall.api"
local uci = api.uci local uci = api.uci
local appname = "passwall" local appname = "passwall"
local has_ss = api.is_finded("ss-redir")
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")
local has_xray = api.finded_com("xray") local has_xray = api.finded_com("xray")
@@ -12,10 +11,6 @@ local vmess_type = {}
local vless_type = {} local vless_type = {}
local hysteria2_type = {} local hysteria2_type = {}
local xray_version = api.get_app_version("xray") local xray_version = api.get_app_version("xray")
if has_ss then
local s = "shadowsocks-libev"
table.insert(ss_type, s)
end
if has_ss_rust then if has_ss_rust then
local s = "shadowsocks-rust" local s = "shadowsocks-rust"
table.insert(ss_type, s) table.insert(ss_type, s)
@@ -16,7 +16,6 @@ end
m:append(Template(appname .. "/cbi/nodes_listvalue_com")) m:append(Template(appname .. "/cbi/nodes_listvalue_com"))
local has_ss = api.is_finded("ss-redir")
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")
local has_xray = api.finded_com("xray") local has_xray = api.finded_com("xray")
@@ -27,10 +26,6 @@ local vmess_type = {}
local vless_type = {} local vless_type = {}
local hysteria2_type = {} local hysteria2_type = {}
local xray_version = api.get_app_version("xray") local xray_version = api.get_app_version("xray")
if has_ss then
local s = "shadowsocks-libev"
table.insert(ss_type, s)
end
if has_ss_rust then if has_ss_rust then
local s = "shadowsocks-rust" local s = "shadowsocks-rust"
table.insert(ss_type, s) table.insert(ss_type, s)
@@ -262,6 +262,13 @@ if load_balancing_options then -- [[ Load balancing Start ]]
o.default = "2" o.default = "2"
o.placeholder = "2" o.placeholder = "2"
o.description = translate("The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them.") o.description = translate("The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them.")
o = s:option(Value, _n("tolerance"), translate("Failure Tolerance (%)"))
o:depends({ [_n("balancingStrategy")] = "leastLoad" })
o.datatype = "uinteger"
o.default = "10"
o.placeholder = "10"
o.description = translate("The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate.")
end -- [[ 负载均衡 End ]] end -- [[ 负载均衡 End ]]
if load_iface_options then -- [[ 自定义接口 Start ]] if load_iface_options then -- [[ 自定义接口 Start ]]
@@ -43,6 +43,8 @@ local security_list = { "none", "auto", "aes-128-gcm", "chacha20-poly1305", "zer
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}'")
local singbox_version = api.get_app_version("sing-box"):match("[^v]+")
o = s:option(ListValue, _n("protocol"), translate("Protocol")) o = s:option(ListValue, _n("protocol"), translate("Protocol"))
o:value("socks", "Socks") o:value("socks", "Socks")
o:value("http", "HTTP") o:value("http", "HTTP")
@@ -351,13 +353,28 @@ end
if singbox_tags:find("with_quic") then if singbox_tags:find("with_quic") then
o = s:option(Value, _n("hysteria2_hop"), translate("Port hopping range")) o = s:option(Value, _n("hysteria2_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:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o = s:option(Value, _n("hysteria2_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, _n("hysteria2_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:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o = s:option(Flag, _n("hysteria2_realms"), translate("Realms"))
o.default = "0"
if api.compare_versions(singbox_version, ">=", "1.14.0") then
o:depends({ [_n("protocol")] = "hysteria2"})
else
o:depends({ [_n("protocol")] = "__hide"})
end
o = s:option(Value, _n("hysteria2_realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ [_n("hysteria2_realms")] = "1" })
o = s:option(DynamicList, _n("hysteria2_realm_stun"), translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o:depends({ [_n("hysteria2_realms")] = "1" })
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password")) o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o.password = true o.password = true
@@ -479,7 +496,7 @@ o.default = "0"
o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false }) o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false })
o:depends({ [_n("protocol")] = "tuic" }) o:depends({ [_n("protocol")] = "tuic" })
o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ [_n("protocol")] = "hysteria" })
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o:depends({ [_n("protocol")] = "naive" }) o:depends({ [_n("protocol")] = "naive" })
o = s:option(TextValue, _n("ech_config"), translate("ECH Config")) o = s:option(TextValue, _n("ech_config"), translate("ECH Config"))
@@ -787,10 +804,14 @@ local protocols = s.fields[_n("protocol")].keylist
if #protocols > 0 then if #protocols > 0 then
for i, v in ipairs(protocols) do for i, v in ipairs(protocols) do
if not v:find("^_") then if not v:find("^_") then
s.fields[_n("address")]:depends({ [_n("protocol")] = v }) local depends_condition = { [_n("protocol")] = v }
s.fields[_n("port")]:depends({ [_n("protocol")] = v }) if v == "hysteria2" then
s.fields[_n("domain_resolver")]:depends({ [_n("protocol")] = v }) depends_condition[_n("hysteria2_realms")] = false
s.fields[_n("domain_strategy")]:depends({ [_n("protocol")] = v }) end
s.fields[_n("address")]:depends(depends_condition)
s.fields[_n("port")]:depends(depends_condition)
s.fields[_n("domain_resolver")]:depends(depends_condition)
s.fields[_n("domain_strategy")]:depends(depends_condition)
end end
end end
end end
@@ -805,6 +826,7 @@ if not load_shunt_options then
o:value("1", translate("Preproxy Node")) o:value("1", translate("Preproxy Node"))
end end
o:value("2", translate("Landing Node")) o:value("2", translate("Landing Node"))
o:depends({ [_n("hysteria2_realms")] = false })
o1 = s:option(ListValue, _n("preproxy_node"), translate("Preproxy Node"), translate("Only support a layer of proxy.")) o1 = s:option(ListValue, _n("preproxy_node"), translate("Preproxy Node"), translate("Only support a layer of proxy."))
o1:depends({ [_n("chain_proxy")] = "1" }) o1:depends({ [_n("chain_proxy")] = "1" })
@@ -1,66 +0,0 @@
local m, s = ...
if not api.is_finded("ss-local") and not api.is_finded("ss-redir") then
return
end
type_name = "SS"
-- [[ Shadowsocks Libev ]]
s.fields["type"]:value(type_name, "Shadowsocks Libev")
if s.val["type"] ~= type_name then
return
end
local option_prefix = "ss_"
local function _n(name)
return option_prefix .. name
end
local ss_encrypt_method_list = {
"rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr",
"aes-192-ctr", "aes-256-ctr", "bf-cfb", "salsa20", "chacha20", "chacha20-ietf",
"aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305",
"xchacha20-ietf-poly1305"
}
o = s:option(ListValue, _n("del_protocol")) --始终隐藏,用于删除 protocol
o:depends({ [_n("__hide")] = "1" })
o.rewrite_option = "protocol"
o = s:option(Value, _n("address"), translate("Address (Support Domain Name)"))
o = s:option(Value, _n("port"), translate("Port"))
o.datatype = "port"
o = s:option(Value, _n("password"), translate("Password"))
o.password = true
o = s:option(Value, _n("method"), translate("Encrypt Method"))
for a, t in ipairs(ss_encrypt_method_list) do o:value(t) end
o = s:option(Value, _n("timeout"), translate("Connection Timeout"))
o.datatype = "uinteger"
o.default = 300
o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"), translate("Need node support required"))
o.default = 0
o = s:option(Flag, _n("plugin_enabled"), translate("plugin"))
o.default = 0
o = s:option(ListValue, _n("plugin"), "SIP003 " .. translate("plugin"))
o.default = "none"
o:value("none", translate("none"))
if api.is_finded("xray-plugin") then o:value("xray-plugin") end
if api.is_finded("v2ray-plugin") then o:value("v2ray-plugin") end
if api.is_finded("obfs-local") then o:value("obfs-local") end
o:depends({ [_n("plugin_enabled")] = true })
o = s:option(Value, _n("plugin_opts"), translate("opts"))
o:depends({ [_n("plugin_enabled")] = true })
api.luci_types(arg[1], m, s, type_name, option_prefix)
@@ -24,6 +24,7 @@ o = s:option(Flag, _n("custom"), translate("Use Custom Config"))
o = s:option(Value, _n("port"), translate("Listen Port")) o = s:option(Value, _n("port"), translate("Listen Port"))
o.datatype = "port" o.datatype = "port"
o.rmempty = false
o:depends({ [_n("custom")] = false }) o:depends({ [_n("custom")] = false })
o = s:option(Flag, _n("realms"), translate("Realms")) o = s:option(Flag, _n("realms"), translate("Realms"))
@@ -55,6 +55,7 @@ o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("port"), translate("Listen Port")) o = s:option(Value, _n("port"), translate("Listen Port"))
o.datatype = "port" o.datatype = "port"
o.rmempty = false
o:depends({ [_n("custom")] = false }) o:depends({ [_n("custom")] = false })
o = s:option(Flag, _n("auth"), translate("Auth")) o = s:option(Flag, _n("auth"), translate("Auth"))
@@ -154,6 +155,17 @@ if singbox_tags:find("with_quic") then
end end
if singbox_tags:find("with_quic") then if singbox_tags:find("with_quic") then
o = s:option(Flag, _n("hysteria2_realms"), translate("Realms"))
o.default = "0"
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(Value, _n("hysteria2_realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ [_n("hysteria2_realms")] = "1" })
o = s:option(DynamicList, _n("hysteria2_realm_stun"), translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o:depends({ [_n("hysteria2_realms")] = "1" })
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password")) o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o.password = true o.password = true
o:depends({ [_n("protocol")] = "hysteria2"}) o:depends({ [_n("protocol")] = "hysteria2"})
@@ -321,7 +333,7 @@ o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false })
o:depends({ [_n("protocol")] = "naive" }) o:depends({ [_n("protocol")] = "naive" })
o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ [_n("protocol")] = "hysteria" })
o:depends({ [_n("protocol")] = "tuic" }) o:depends({ [_n("protocol")] = "tuic" })
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o = s:option(TextValue, _n("ech_key"), translate("ECH Key")) o = s:option(TextValue, _n("ech_key"), translate("ECH Key"))
o.default = "" o.default = ""
@@ -1,78 +0,0 @@
local m, s = ...
local api = require "luci.passwall.api"
if not api.is_finded("ss-server") then
return
end
local type_name = "SS"
local option_prefix = "ss_"
local function _n(name)
return option_prefix .. name
end
local ss_encrypt_method_list = {
"rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr",
"aes-192-ctr", "aes-256-ctr", "bf-cfb", "camellia-128-cfb",
"camellia-192-cfb", "camellia-256-cfb", "salsa20", "chacha20",
"chacha20-ietf", -- aead
"aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305",
"xchacha20-ietf-poly1305"
}
-- [[ Shadowsocks ]]
s.fields["type"]:value(type_name, translate("Shadowsocks"))
o = s:option(Flag, _n("custom"), translate("Use Custom Config"))
o = s:option(Value, _n("port"), translate("Listen Port"))
o.datatype = "port"
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("password"), translate("Password"))
o.password = true
o:depends({ [_n("custom")] = false })
o = s:option(ListValue, _n("method"), translate("Encrypt Method"))
for a, t in ipairs(ss_encrypt_method_list) do o:value(t) end
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("timeout"), translate("Connection Timeout"))
o.datatype = "uinteger"
o.default = 300
o:depends({ [_n("custom")] = false })
o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"))
o.default = "0"
o:depends({ [_n("custom")] = false })
o = s:option(TextValue, _n("custom_config"), translate("Custom Config"))
o.rows = 10
o.wrap = "off"
o:depends({ [_n("custom")] = true })
o.validate = function(self, value, t)
if value and api.jsonc.parse(value) then
return value
else
return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!")
end
end
o.custom_cfgvalue = function(self, section, value)
local config_str = m:get(section, "config_str")
if config_str then
return api.base64Decode(config_str)
end
end
o.custom_write = function(self, section, value)
m:set(section, "config_str", api.base64Encode(value) or "")
end
o = s:option(Flag, _n("log"), translate("Log"))
o.default = "1"
o.rmempty = false
api.luci_types(arg[1], m, s, type_name, option_prefix)
+34 -3
View File
@@ -472,9 +472,10 @@ function get_valid_nodes()
end end
end end
local port = e.port or e.hysteria_hop or e.hysteria2_hop local port = e.port or e.hysteria_hop or e.hysteria2_hop
if (port and e.address) or e.hysteria2_realms then local is_realm = (e.type == "Hysteria2" or e.protocol == 'hysteria2') and e.hysteria2_realms or nil
if (port and e.address) or is_realm then
local address = e.address local address = e.address
if is_ip(address) or datatypes.hostname(address) or e.hysteria2_realms then if is_ip(address) or datatypes.hostname(address) or is_realm then
if (e.type == "sing-box" or e.type == "Xray") and e.protocol then if (e.type == "sing-box" or e.type == "Xray") and e.protocol then
local protocol = e.protocol local protocol = e.protocol
if protocol == "vmess" then if protocol == "vmess" then
@@ -501,10 +502,11 @@ function get_valid_nodes()
type_name = type_name .. " " .. protocol type_name = type_name .. " " .. protocol
end end
if is_ipv6(address) then address = get_ipv6_full(address) end if is_ipv6(address) then address = get_ipv6_full(address) end
type_name = is_realm and type_name .. " Realm" or type_name
e["remark"] = trim("%s:[%s]" % {type_name, e.remarks}) e["remark"] = trim("%s:[%s]" % {type_name, e.remarks})
if show_node_info == "1" then if show_node_info == "1" then
port = (port or ""):gsub(":", "-") port = (port or ""):gsub(":", "-")
if not e.hysteria2_realms then if not is_realm then
e["remark"] = trim("%s:[%s] %s:%s" % {type_name, e.remarks, address, port}) e["remark"] = trim("%s:[%s] %s:%s" % {type_name, e.remarks, address, port})
end end
end end
@@ -596,6 +598,9 @@ function get_node_remarks(n)
end end
type_name = type_name .. " " .. protocol type_name = type_name .. " " .. protocol
end end
if (n.type == "Hysteria2" or n.protocol == 'hysteria2') and n.hysteria2_realms then
type_name = type_name .. " Realm"
end
remarks = trim("%s:[%s]" % {type_name, n.remarks}) remarks = trim("%s:[%s]" % {type_name, n.remarks})
end end
end end
@@ -1595,3 +1600,29 @@ function vps_domain_exclude(domain)
if map[domain] then return true end if map[domain] then return true end
return false return false
end end
function parse_realm_uri(uri)
if type(uri) ~= "string" then return nil end
-- realm://token@server/realm_id?query
local token, server_url, realm_id, query = trim(uri):match("^realm://([^@]+)@([^/]+)/([^?]*)%??(.*)$")
if not token or not server_url or not realm_id then return nil end
realm_id = realm_id:gsub("/+$", "")
local realm = {
token = token,
server_url = server_url,
realm_id = realm_id
}
-- 解析 query 中的 stun=
if query and query ~= "" then
local stun_servers = {}
for key, value in query:gmatch("([^&=?]+)=([^&]+)") do
if key == "stun" and value ~= "" then
stun_servers[#stun_servers + 1] = value
end
end
if #stun_servers > 0 then
realm.stun_servers = stun_servers
end
end
return realm
end
@@ -132,7 +132,7 @@ local function start()
end end
end end
bin = ln_run("/usr/bin/microsocks", "microsocks_" .. id, string.format("-i :: -p %s %s", port, auth), log_path) bin = ln_run("/usr/bin/microsocks", "microsocks_" .. id, string.format("-i :: -p %s %s", port, auth), log_path)
elseif type == "SS" or type == "SSR" then elseif type == "SSR" then
if user.custom == "1" and user.config_str then if user.custom == "1" and user.config_str then
config = jsonc.parse(api.base64Decode(user.config_str)) config = jsonc.parse(api.base64Decode(user.config_str))
else else
@@ -67,11 +67,7 @@ function gen_config(var)
server_host = api.get_ipv6_full(server_host) server_host = api.get_ipv6_full(server_host)
end end
local server = server_host .. (server_port and ":" .. server_port or "") local server = server_host .. ":" .. ((server_port or "") .. "," .. (node.hysteria2_hop or "")):gsub("^[%s,]+", ""):gsub("[%s,]+$", ""):gsub(":", "-")
if node.hysteria2_hop then
server = server .. (server_port and "," or ":") .. string.gsub(node.hysteria2_hop, ":", "-")
end
local config = { local config = {
server = (function() server = (function()
@@ -80,11 +80,7 @@ function gen_config(var)
tcp_tproxy = var["tcp_tproxy"] and true or nil tcp_tproxy = var["tcp_tproxy"] and true or nil
} }
if node.type == "SS" then if node.type == "SSR" then
config.plugin = plugin_file or nil
config.plugin_opts = (plugin_file) and node.plugin_opts or nil
config.mode = mode
elseif node.type == "SSR" then
config.protocol = node.protocol config.protocol = node.protocol
config.protocol_param = node.protocol_param config.protocol_param = node.protocol_param
config.obfs = node.obfs config.obfs = node.obfs
@@ -103,8 +103,8 @@ function gen_outbound(flag, node, tag, proxy_table)
local run_socks_instance = true 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 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 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 run_socks_instance = proxy_table.run_socks_instance
end end
@@ -472,6 +472,7 @@ function gen_outbound(flag, node, tag, proxy_table)
table.insert(server_ports, range) table.insert(server_ports, range)
end end
end end
result.server_port = nil
end end
protocol_table = { protocol_table = {
server_ports = next(server_ports) and server_ports or nil, server_ports = next(server_ports) and server_ports or nil,
@@ -528,6 +529,10 @@ function gen_outbound(flag, node, tag, proxy_table)
table.insert(server_ports, range) table.insert(server_ports, range)
end end
end end
result.server_port = nil
end
if node.hysteria2_realms then
server_ports = {}
end end
local interval, interval_max local interval, interval_max
if next(server_ports) then if next(server_ports) then
@@ -571,7 +576,18 @@ function gen_outbound(flag, node, tag, proxy_table)
return (t and t >= 2 and t <= 60) and t or nil return (t and t >= 2 and t <= 60) and t or nil
end)(node.hysteria2_keep_alive_period), end)(node.hysteria2_keep_alive_period),
disable_path_mtu_discovery = version_ge_1_14_0 and (tonumber(node.hysteria2_disable_mtu_discovery) == 1) or nil, disable_path_mtu_discovery = version_ge_1_14_0 and (tonumber(node.hysteria2_disable_mtu_discovery) == 1) or nil,
tls = tls tls = tls,
realm = node.hysteria2_realms and (function()
result.server = nil
result.server_port = nil
local realm = api.parse_realm_uri(node.hysteria2_realm_url)
if realm then
realm.server_url = realm.server_url and "https://" .. realm.server_url or nil
realm.stun_servers = realm.stun_servers or node.hysteria2_realm_stun
return realm
end
return nil
end)() or nil
} }
end end
@@ -908,7 +924,17 @@ function gen_config_server(node)
} }
}, },
ignore_client_bandwidth = (node.hysteria2_ignore_client_bandwidth == "1") and true or false, ignore_client_bandwidth = (node.hysteria2_ignore_client_bandwidth == "1") and true or false,
tls = tls tls = tls,
realm = node.hysteria2_realms and (function()
local realm = api.parse_realm_uri(node.hysteria2_realm_url)
if realm then
realm.server_url = realm.server_url and "https://" .. realm.server_url or nil
realm.stun_servers = realm.stun_servers or node.hysteria2_realm_stun
realm.stun_domain_resolver = "direct"
return realm
end
return nil
end)() or nil
} }
end end
@@ -2088,7 +2114,7 @@ function gen_config(var)
if not value.detour and value.server then if not value.detour and value.server then
value.detour = "direct" value.detour = "direct"
end end
if value.server and not api.datatypes.hostname(value.server) then if (value.server and not api.datatypes.hostname(value.server)) and not value.realm then
value.domain_resolver = nil value.domain_resolver = nil
end end
for k, v in pairs(config.outbounds[index]) do for k, v in pairs(config.outbounds[index]) do
@@ -1072,7 +1072,13 @@ function gen_config(var)
type = _node.balancingStrategy, type = _node.balancingStrategy,
settings = { settings = {
expected = _node.expected and tonumber(_node.expected) and tonumber(_node.expected) or 2, expected = _node.expected and tonumber(_node.expected) and tonumber(_node.expected) or 2,
maxRTT = "1s" maxRTT = "1s",
tolerance = (function(t)
t = tonumber(t) or 0
if t < 1 then return nil end
if t > 100 then t = 100 end
return t / 100
end)(_node.tolerance)
} }
} }
else else
@@ -4,13 +4,12 @@ local api = require "luci.passwall.api"
local appname = api.appname local appname = api.appname
local map = self.map local map = self.map
local has_ss = api.is_finded("ss-redir")
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")
local has_xray = api.finded_com("xray") local has_xray = api.finded_com("xray")
local has_hysteria2 = api.finded_com("hysteria") local has_hysteria2 = api.finded_com("hysteria")
local ss_type = api.get_core("ss_type", {{has_ss,"shadowsocks-libev"},{has_ss_rust,"shadowsocks-rust"},{has_singbox,"sing-box"},{has_xray,"xray"}}) local ss_type = api.get_core("ss_type", {{has_ss_rust,"shadowsocks-rust"},{has_singbox,"sing-box"},{has_xray,"xray"}})
local trojan_type = api.get_core("trojan_type", {{has_singbox,"sing-box"},{has_xray,"xray"}}) local trojan_type = api.get_core("trojan_type", {{has_singbox,"sing-box"},{has_xray,"xray"}})
local vmess_type = api.get_core("vmess_type", {{has_xray,"xray"},{has_singbox,"sing-box"}}) local vmess_type = api.get_core("vmess_type", {{has_xray,"xray"},{has_singbox,"sing-box"}})
local vless_type = api.get_core("vless_type", {{has_xray,"xray"},{has_singbox,"sing-box"}}) local vless_type = api.get_core("vless_type", {{has_xray,"xray"},{has_singbox,"sing-box"}})
@@ -165,10 +164,7 @@ local current_node = map:get(section)
var dom_prefix = null var dom_prefix = null
var protocol = "" var protocol = ""
if (v_type === "SS") { if (v_type === "SS-Rust") {
dom_prefix = "ss_"
protocol = "ss"
} else if (v_type === "SS-Rust") {
dom_prefix = "ssrust_" dom_prefix = "ssrust_"
protocol = "ss" protocol = "ss"
} else if (v_type === "SSR") { } else if (v_type === "SSR") {
@@ -196,7 +192,7 @@ local current_node = map:get(section)
} }
} }
var url = null; var url = null;
if (v_type === "SS" || v_type === "SS-Rust" || ((v_type === "sing-box" || v_type === "Xray") && opt.get(dom_prefix + "protocol").value === "shadowsocks")) { if (v_type === "SS-Rust" || ((v_type === "sing-box" || v_type === "Xray") && opt.get(dom_prefix + "protocol").value === "shadowsocks")) {
protocol = "ss" protocol = "ss"
var v_port = opt.get(dom_prefix + "port"); var v_port = opt.get(dom_prefix + "port");
var v_method = opt.get(dom_prefix + "method") || opt.get(dom_prefix + "ss_method"); var v_method = opt.get(dom_prefix + "method") || opt.get(dom_prefix + "ss_method");
@@ -1000,17 +996,9 @@ local current_node = map:get(section)
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 if (ss_type == "shadowsocks-rust") { } else {
dom_prefix = "ssrust_" dom_prefix = "ssrust_"
opt.set('type', "SS-Rust"); opt.set('type', "SS-Rust");
} else {
if (["2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"].includes(method)) {
dom_prefix = "ssrust_"
opt.set('type', "SS-Rust");
} else {
dom_prefix = "ss_"
opt.set('type', "SS");
}
} }
const _method = (method || "none").toLowerCase(); const _method = (method || "none").toLowerCase();
@@ -910,7 +910,11 @@ table td, .table .td {
} else { } else {
port_s = o["hysteria_hop"] || o["hysteria2_hop"]; port_s = o["hysteria_hop"] || o["hysteria2_hop"];
} }
str += type + ":" + remarks; if ((o.type === "Hysteria2" || o.protocol === 'hysteria2') && o.hysteria2_realms) {
str += type + " Realm:" + remarks;
} else {
str += type + ":" + remarks;
}
return str; return str;
} }
@@ -952,7 +956,7 @@ table td, .table .td {
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;
if (auto_detection_time != "icmp" && o["address"] && o["port"]) { if (auto_detection_time != "icmp" && o["address"]) {
innerHTML = innerHTML.split("{{ping}}").join('<span class="ping"><a href="javascript:void(0)" onclick="javascript:ping_node(\'{{id}}\', this, \'icmp\')"><%:Test%></a></span>'); innerHTML = innerHTML.split("{{ping}}").join('<span class="ping"><a href="javascript:void(0)" onclick="javascript:ping_node(\'{{id}}\', this, \'icmp\')"><%:Test%></a></span>');
} else { } else {
innerHTML = innerHTML.split("{{ping}}").join('<span class="ping_value" cbiid="{{id}}"><a href="javascript:void(0)" style="color:inherit">---</a></span>'); innerHTML = innerHTML.split("{{ping}}").join('<span class="ping_value" cbiid="{{id}}"><a href="javascript:void(0)" style="color:inherit">---</a></span>');
@@ -962,7 +966,8 @@ 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>');
} }
if (o["protocol"] && o["protocol"] === '_shunt') { let is_realm = (o.type === "Hysteria2" || o.protocol === 'hysteria2') && o.hysteria2_realms || false;
if (o["protocol"] === '_shunt' || is_realm) {
innerHTML = innerHTML.split("{{url_test}}").join('<span class="ping" cbiid="{{id}}">---</span>'); innerHTML = innerHTML.split("{{url_test}}").join('<span class="ping" cbiid="{{id}}">---</span>');
} else { } 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>');
@@ -971,11 +976,13 @@ table td, .table .td {
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") {
if (o["address"] && o["port"]) { let _port = o["port"] || o["hysteria_hop"] || o["hysteria2_hop"];
if (_port) _port = _port.replace(/:/g, '-');
if (o["address"] && _port) {
let _address = o["address"] let _address = o["address"]
if (o["full_address"]) if (o["full_address"])
_address = o["full_address"] _address = o["full_address"]
node_remarks += "<br>" + _address + ":" + o["port"] node_remarks += "<br>" + _address + ":" + _port
} }
} }
innerHTML = innerHTML.split("{{remarks}}").join(node_remarks); innerHTML = innerHTML.split("{{remarks}}").join(node_remarks);
+6
View File
@@ -577,6 +577,12 @@ msgstr "优选节点数量"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them." msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
msgstr "负载均衡器选出最优节点的个数,流量将在这几个节点中随机分配。" msgstr "负载均衡器选出最优节点的个数,流量将在这几个节点中随机分配。"
msgid "Failure Tolerance (%)"
msgstr "测速失败率容忍度(%)"
msgid "The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate."
msgstr "最多可接受的测速失败率,例如 1 表示允许 1% 的测速失败率。"
msgid "Shunt" msgid "Shunt"
msgstr "分流" msgstr "分流"
@@ -374,7 +374,7 @@ run_socks() {
fi fi
fi fi
if [ -n "${error_msg}" ] && [ "$(config_n_get $node hysteria2_realms)" = "1" ]; then if [ -n "${error_msg}" ] && ([ -n "$(config_n_get $node hysteria_hop)" ] || [ -n "$(config_n_get $node hysteria2_hop)" ] || [ "$(config_n_get $node hysteria2_realms)" = "1" ]); then
unset error_msg unset error_msg
fi fi
@@ -463,17 +463,6 @@ run_socks() {
lua $UTIL_SS gen_config "$(json_dump)" > $config_file lua $UTIL_SS gen_config "$(json_dump)" > $config_file
[ -n "$no_run" ] || ln_run "$(first_type ssr-local)" "ssr-local" $log_file -c "$config_file" -v -u [ -n "$no_run" ] || ln_run "$(first_type ssr-local)" "ssr-local" $log_file -c "$config_file" -v -u
;; ;;
ss)
[ -n "$no_run" ] || {
local plugin_sh="${config_file%.json}_plugin.sh"
json_add_string "plugin_sh" "$plugin_sh"
}
json_add_string "local_addr" "$bind"
json_add_string "local_port" "$socks_port"
json_add_string "mode" "tcp_and_udp"
lua $UTIL_SS gen_config "$(json_dump)" > $config_file
[ -n "$no_run" ] || ln_run "$(first_type ss-local)" "ss-local" $log_file -c "$config_file" -v
;;
ss-rust) ss-rust)
[ "$http_port" != "0" ] && { [ "$http_port" != "0" ] && {
http_flag=1 http_flag=1
@@ -632,15 +621,6 @@ run_redir() {
lua $UTIL_SS gen_config "$(json_dump)" > $config_file lua $UTIL_SS gen_config "$(json_dump)" > $config_file
ln_run "$(first_type ssr-redir)" "ssr-redir" $log_file -c "$config_file" -v -U ln_run "$(first_type ssr-redir)" "ssr-redir" $log_file -c "$config_file" -v -U
;; ;;
ss)
local plugin_sh="${config_file%.json}_plugin.sh"
json_add_string "plugin_sh" "$plugin_sh"
json_add_string "local_addr" "0.0.0.0"
json_add_string "local_port" "$local_port"
json_add_string "mode" "udp_only"
lua $UTIL_SS gen_config "$(json_dump)" > $config_file
ln_run "$(first_type ss-redir)" "ss-redir" $log_file -c "$config_file" -v
;;
ss-rust) ss-rust)
local plugin_sh="${config_file%.json}_plugin.sh" local plugin_sh="${config_file%.json}_plugin.sh"
json_add_string "plugin_sh" "$plugin_sh" json_add_string "plugin_sh" "$plugin_sh"
@@ -889,23 +869,6 @@ run_redir() {
lua $UTIL_SS gen_config "$(json_dump)" > $config_file lua $UTIL_SS gen_config "$(json_dump)" > $config_file
ln_run "$(first_type ssr-redir)" "ssr-redir" $log_file -c "$config_file" -v ${_extra_param} ln_run "$(first_type ssr-redir)" "ssr-redir" $log_file -c "$config_file" -v ${_extra_param}
;; ;;
ss)
[ "${TCP_PROXY_WAY}" = "tproxy" ] && json_add_string "tcp_tproxy" "true"
if [ "$TCP_UDP" = "1" ]; then
config_file="${config_file//TCP/TCP_UDP}"
UDP_REDIR_PORT=$TCP_REDIR_PORT
unset UDP_NODE
json_add_string "mode" "tcp_and_udp"
else
json_add_string "mode" "tcp_only"
fi
local plugin_sh="${config_file%.json}_plugin.sh"
json_add_string "plugin_sh" "$plugin_sh"
json_add_string "local_addr" "0.0.0.0"
json_add_string "local_port" "$local_port"
lua $UTIL_SS gen_config "$(json_dump)" > $config_file
ln_run "$(first_type ss-redir)" "ss-redir" $log_file -c "$config_file" -v
;;
ss-rust) ss-rust)
json_add_string "local_tcp_redir_port" "$local_port" json_add_string "local_tcp_redir_port" "$local_port"
[ "${TCP_PROXY_WAY}" = "tproxy" ] && json_add_string "tcp_tproxy" "true" [ "${TCP_PROXY_WAY}" = "tproxy" ] && json_add_string "tcp_tproxy" "true"
@@ -2049,8 +2012,8 @@ get_config() {
FILTER_PROXY_IPV6=$(config_t_get global filter_proxy_ipv6 0) FILTER_PROXY_IPV6=$(config_t_get global filter_proxy_ipv6 0)
DNS_REDIRECT=$(config_t_get global dns_redirect 1) DNS_REDIRECT=$(config_t_get global dns_redirect 1)
REDIRECT_LIST="socks ss ss-rust ssr sing-box xray naiveproxy hysteria2" REDIRECT_LIST="socks ss-rust ssr sing-box xray naiveproxy hysteria2"
TPROXY_LIST="socks ss ss-rust ssr sing-box xray hysteria2" TPROXY_LIST="socks ss-rust ssr sing-box xray hysteria2"
NEXT_DNS_LISTEN_PORT=15353 NEXT_DNS_LISTEN_PORT=15353
TUN_DNS="127.0.0.1#${NEXT_DNS_LISTEN_PORT}" TUN_DNS="127.0.0.1#${NEXT_DNS_LISTEN_PORT}"
@@ -24,7 +24,6 @@ local UrlDecode = api.UrlDecode
local uci = api.uci local uci = api.uci
local fs = api.fs local fs = api.fs
local has_ss = api.is_finded("ss-redir")
local has_ss_rust = api.is_finded("sslocal") local has_ss_rust = api.is_finded("sslocal")
local has_ssr = api.is_finded("ssr-local") and api.is_finded("ssr-redir") local has_ssr = api.is_finded("ssr-local") and api.is_finded("ssr-redir")
local has_singbox = api.finded_com("sing-box") local has_singbox = api.finded_com("sing-box")
@@ -35,7 +34,7 @@ local DEFAULT_FILTER_KEYWORD_MODE = uci:get(appname, "@global_subscribe[0]", "fi
local DEFAULT_FILTER_KEYWORD_DISCARD_LIST = uci:get(appname, "@global_subscribe[0]", "filter_discard_list") or {} local DEFAULT_FILTER_KEYWORD_DISCARD_LIST = uci:get(appname, "@global_subscribe[0]", "filter_discard_list") or {}
local DEFAULT_FILTER_KEYWORD_KEEP_LIST = uci:get(appname, "@global_subscribe[0]", "filter_keep_list") or {} local DEFAULT_FILTER_KEYWORD_KEEP_LIST = uci:get(appname, "@global_subscribe[0]", "filter_keep_list") or {}
-- 取节点使用core类型(节点订阅页面未设置时,自动取默认) -- 取节点使用core类型(节点订阅页面未设置时,自动取默认)
local DEFAULT_SS_TYPE = api.get_core("ss_type", {{has_ss,"shadowsocks-libev"},{has_ss_rust,"shadowsocks-rust"},{has_singbox,"sing-box"},{has_xray,"xray"}}) local DEFAULT_SS_TYPE = api.get_core("ss_type", {{has_ss_rust,"shadowsocks-rust"},{has_singbox,"sing-box"},{has_xray,"xray"}})
local DEFAULT_TROJAN_TYPE = api.get_core("trojan_type", {{has_singbox,"sing-box"},{has_xray,"xray"}}) local DEFAULT_TROJAN_TYPE = api.get_core("trojan_type", {{has_singbox,"sing-box"},{has_xray,"xray"}})
local DEFAULT_VMESS_TYPE = api.get_core("vmess_type", {{has_xray,"xray"},{has_singbox,"sing-box"}}) local DEFAULT_VMESS_TYPE = api.get_core("vmess_type", {{has_xray,"xray"},{has_singbox,"sing-box"}})
local DEFAULT_VLESS_TYPE = api.get_core("vless_type", {{has_xray,"xray"},{has_singbox,"sing-box"}}) local DEFAULT_VLESS_TYPE = api.get_core("vless_type", {{has_xray,"xray"},{has_singbox,"sing-box"}})
@@ -43,7 +42,6 @@ local DEFAULT_HYSTERIA2_TYPE = api.get_core("hysteria2_type", {{has_hysteria2,"h
local core_has = { local core_has = {
["xray"] = has_xray, ["xray"] = has_xray,
["sing-box"] = has_singbox, ["sing-box"] = has_singbox,
["shadowsocks-libev"] = has_ss,
["shadowsocks-rust"] = has_ss_rust, ["shadowsocks-rust"] = has_ss_rust,
["hysteria2"] = has_hysteria2 ["hysteria2"] = has_hysteria2
} }
@@ -482,9 +480,7 @@ end
-- 设置 ss 协议实现类型 -- 设置 ss 协议实现类型
local function set_ss_implementation(ss_type, result) local function set_ss_implementation(ss_type, result)
if ss_type == "shadowsocks-libev" and has_ss then if ss_type == "shadowsocks-rust" and has_ss_rust then
result.type = "SS"
elseif ss_type == "shadowsocks-rust" and has_ss_rust then
result.type = 'SS-Rust' result.type = 'SS-Rust'
elseif ss_type == "xray" and has_xray then elseif ss_type == "xray" and has_xray then
result.type = 'Xray' result.type = 'Xray'
@@ -835,20 +831,6 @@ local function processData(szType, content, add_mode, group, sub_cfg)
end end
end end
if result.type == "SS" then
local aead2022_methods = { "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" }
local aead2022 = false
for k, v in ipairs(aead2022_methods) do
if method:lower() == v:lower() then
aead2022 = true
end
end
if aead2022 then
-- shadowsocks-libev 不支持2022加密
result.error_msg = "shadowsocks-libev 不支持2022加密。"
end
end
if params.type then if params.type then
params.type = string.lower(params.type) params.type = string.lower(params.type)
if result.type == "sing-box" and params.type == "raw" then if result.type == "sing-box" and params.type == "raw" then
@@ -862,7 +844,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
else else
result.transport = params.type result.transport = params.type
end end
if result.type ~= "SS-Rust" and result.type ~= "SS" then if result.type ~= "SS-Rust" then
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
+2 -2
View File
@@ -6,8 +6,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall2 PKG_NAME:=luci-app-passwall2
PKG_VERSION:=26.5.1 PKG_VERSION:=26.5.18
PKG_RELEASE:=43 PKG_RELEASE:=44
PKG_PO_VERSION:=$(PKG_VERSION) PKG_PO_VERSION:=$(PKG_VERSION)
PKG_CONFIG_DEPENDS:= \ PKG_CONFIG_DEPENDS:= \
@@ -52,7 +52,7 @@ function index()
entry({"admin", "services", appname, "socks_config"}, cbi(appname .. "/client/socks_config")).leaf = true entry({"admin", "services", appname, "socks_config"}, cbi(appname .. "/client/socks_config")).leaf = true
entry({"admin", "services", appname, "acl"}, cbi(appname .. "/client/acl"), _("Access control"), 98).leaf = true entry({"admin", "services", appname, "acl"}, cbi(appname .. "/client/acl"), _("Access control"), 98).leaf = true
entry({"admin", "services", appname, "acl_config"}, cbi(appname .. "/client/acl_config")).leaf = true entry({"admin", "services", appname, "acl_config"}, cbi(appname .. "/client/acl_config")).leaf = true
entry({"admin", "services", appname, "log"}, form(appname .. "/client/log"), _("Watch Logs"), 999).leaf = true entry({"admin", "services", appname, "log"}, form(appname .. "/client/log"), _("Runtime Logs"), 999).leaf = true
--[[ Server ]] --[[ Server ]]
entry({"admin", "services", appname, "server"}, cbi(appname .. "/server/index"), _("Server-Side"), 99).leaf = true entry({"admin", "services", appname, "server"}, cbi(appname .. "/server/index"), _("Server-Side"), 99).leaf = true
@@ -508,13 +508,17 @@ function delete_select_nodes()
if t["node"] == w then if t["node"] == w then
uci:delete(appname, t[".name"]) uci:delete(appname, t[".name"])
end end
local changed = false
local auto_switch_node_list = uci:get(appname, t[".name"], "autoswitch_backup_node") or {} local auto_switch_node_list = uci:get(appname, t[".name"], "autoswitch_backup_node") or {}
for i = #auto_switch_node_list, 1, -1 do for i = #auto_switch_node_list, 1, -1 do
if w == auto_switch_node_list[i] then if w == auto_switch_node_list[i] then
table.remove(auto_switch_node_list, i) table.remove(auto_switch_node_list, i)
changed = true
end end
end end
uci:set_list(appname, t[".name"], "autoswitch_backup_node", auto_switch_node_list) if changed then
uci:set_list(appname, t[".name"], "autoswitch_backup_node", auto_switch_node_list)
end
end) end)
uci:foreach(appname, "haproxy_config", function(t) uci:foreach(appname, "haproxy_config", function(t)
if t["lbss"] == w then if t["lbss"] == w then
@@ -21,27 +21,50 @@ local function _n(name)
end end
o = s:option(Value, _n("address"), translate("Address (Support Domain Name)")) o = s:option(Value, _n("address"), translate("Address (Support Domain Name)"))
o:depends({ [_n("realms")] = false })
o = s:option(Value, _n("port"), translate("Port")) o = s:option(Value, _n("port"), translate("Port"))
o.datatype = "port" o.datatype = "port"
o:depends({ [_n("realms")] = false })
o = s:option(Value, _n("hop"), translate("Port hopping range")) o = s:option(Value, _n("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 = o.option o.rewrite_option = o.option
o:depends({ [_n("realms")] = false })
o = s:option(Value, _n("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, _n("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 = o.option o.rewrite_option = o.option
o:depends({ [_n("realms")] = false })
o = s:option(Value, _n("obfs"), translate("Obfs Password")) o = s:option(Flag, _n("realms"), translate("Realms"))
o.default = "0"
o.rewrite_option = o.option o.rewrite_option = o.option
o = s:option(Value, _n("realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o.rewrite_option = o.option
o:depends({ [_n("realms")] = "1" })
o = s:option(DynamicList, _n("realm_stun"), translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o.rewrite_option = o.option
o:depends({ [_n("realms")] = "1" })
o = s:option(Value, _n("auth_password"), translate("Auth Password")) o = s:option(Value, _n("auth_password"), translate("Auth Password"))
o.password = true o.password = true
o.rewrite_option = o.option o.rewrite_option = o.option
o = s:option(ListValue, _n("obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o.rewrite_option = o.option
o = s:option(Value, _n("obfs_password"), translate("Obfs Password"))
o.rewrite_option = o.option
o:depends({ [_n("obfs_type")] = "salamander" })
o = s:option(Flag, _n("fast_open"), translate("Fast Open")) o = s:option(Flag, _n("fast_open"), translate("Fast Open"))
o.default = "0" o.default = "0"
@@ -164,28 +164,66 @@ if load_balancing_options then -- [[ Load balancing Start ]]
-- Fallback Node -- Fallback Node
o = s:option(ListValue, _n("fallback_node"), translate("Fallback Node")) o = s:option(ListValue, _n("fallback_node"), translate("Fallback Node"))
o.group = {"",""}
o:value("", translate("Close(Not use)")) o:value("", translate("Close(Not use)"))
o:value("_direct", translate("Direct Connection"))
o:depends({ [_n("protocol")] = "_balancing" }) o:depends({ [_n("protocol")] = "_balancing" })
o.template = appname .. "/cbi/nodes_listvalue" o.template = appname .. "/cbi/nodes_listvalue"
o.group = {""} -- Maximum number of fallback nesting layers
local function check_fallback_chain(fb) local MAX_FALLBACK_DEPTH = 3
for k, v in pairs(fallback_list) do -- Check if a loop will form.
if v.fallback == fb then local function will_loop(start_id, target_id, depth)
fallback_list[k] = nil depth = depth or 0
check_fallback_chain(v.id) -- Recursion stops after the maximum depth is exceeded.
if depth >= MAX_FALLBACK_DEPTH then
return false
end
for _, v in ipairs(fallback_list) do
if v.id == target_id then
local fb = v.fallback
-- No fallback
if not fb or fb == "" or fb == "_direct" then
return false
end
-- Loopback detected
if fb == start_id then
return true
end
-- Continue recursive checking
return will_loop(start_id, fb, depth + 1)
end end
end end
return false
end end
-- Check the fallback chain and remove the balancer node that would form a closed loop. -- Get fallback chain depth
if is_balancer then local function get_fallback_depth(id, depth)
check_fallback_chain(arg[1]) depth = depth or 0
if depth >= MAX_FALLBACK_DEPTH then
return depth
end
for _, v in ipairs(fallback_list) do
if v.id == id then
local fb = v.fallback
if not fb or fb == "" or fb == "_direct" then
return depth
end
return get_fallback_depth(fb, depth + 1)
end
end
return depth
end end
for i, v in ipairs(fallback_list) do for _, v in ipairs(fallback_list) do
o:value(v.id, v.remark) local depth = get_fallback_depth(v.id)
o.group[#o.group+1] = (v.group and v.group ~= "") and v.group or translate("default") -- Once the maximum number of nested doll layers is exceeded, further selection of the balancer is not allowed.
if depth < MAX_FALLBACK_DEPTH
and not will_loop(arg[1], v.id)
then
o:value(v.id, v.remark)
o.group[#o.group + 1] = (v.group and v.group ~= "") and v.group or translate("default")
end
end end
for k1, v1 in pairs(node_list) do for k1, v1 in pairs(node_list) do
if k1 == "socks_list" or k1 == "normal_list" then if k1 == "socks_list" or k1 == "normal_list" or k1 == "urltest_list" then
for i, v in ipairs(v1) do for i, v in ipairs(v1) do
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")
@@ -221,6 +259,13 @@ if load_balancing_options then -- [[ Load balancing Start ]]
o.default = "2" o.default = "2"
o.placeholder = "2" o.placeholder = "2"
o.description = translate("The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them.") o.description = translate("The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them.")
o = s:option(Value, _n("tolerance"), translate("Failure Tolerance (%)"))
o:depends({ [_n("balancingStrategy")] = "leastLoad" })
o.datatype = "uinteger"
o.default = "10"
o.placeholder = "10"
o.description = translate("The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate.")
end -- [[ Load balancing End ]] end -- [[ Load balancing End ]]
if load_iface_options then -- [[ Custom Interface Start ]] if load_iface_options then -- [[ Custom Interface Start ]]
@@ -299,11 +344,9 @@ o.placeholder = "30"
o.default = "30" o.default = "30"
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps")) o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o:depends({ [_n("protocol")] = "hysteria2" }) o.password = true
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type")) o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable")) o:value("", translate("Disable"))
@@ -313,9 +356,11 @@ o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password")) o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" }) o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password")) o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps"))
o.password = true o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s~120s)") o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s~120s)")
o:depends({ [_n("protocol")] = "hysteria2"}) o:depends({ [_n("protocol")] = "hysteria2"})
@@ -43,6 +43,8 @@ local security_list = { "none", "auto", "aes-128-gcm", "chacha20-poly1305", "zer
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}'")
local singbox_version = api.get_app_version("sing-box"):match("[^v]+")
o = s:option(ListValue, _n("protocol"), translate("Protocol")) o = s:option(ListValue, _n("protocol"), translate("Protocol"))
o:value("socks", "Socks") o:value("socks", "Socks")
o:value("http", "HTTP") o:value("http", "HTTP")
@@ -388,19 +390,32 @@ end
if singbox_tags:find("with_quic") then if singbox_tags:find("with_quic") then
o = s:option(Value, _n("hysteria2_hop"), translate("Port hopping range")) o = s:option(Value, _n("hysteria2_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:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o = s:option(Value, _n("hysteria2_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, _n("hysteria2_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:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps")) o = s:option(Flag, _n("hysteria2_realms"), translate("Realms"))
o:depends({ [_n("protocol")] = "hysteria2" }) o.default = "0"
if api.compare_versions(singbox_version, ">=", "1.14.0") then
o:depends({ [_n("protocol")] = "hysteria2"})
else
o:depends({ [_n("protocol")] = "__hide"})
end
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps")) o = s:option(Value, _n("hysteria2_realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ [_n("hysteria2_realms")] = "1" })
o = s:option(DynamicList, _n("hysteria2_realm_stun"), translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o:depends({ [_n("hysteria2_realms")] = "1" })
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o.password = true
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type")) o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable")) o:value("", translate("Disable"))
@@ -410,8 +425,20 @@ if singbox_tags:find("with_quic") then
o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password")) o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" }) o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password")) o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps"))
o.password = true o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s~120s)")
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(Value, _n("hysteria2_keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Example:") .. "10s (2s~60s)")
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(Flag, _n("hysteria2_disable_mtu_discovery"), translate("Disable MTU detection"))
o.default = "0"
o:depends({ [_n("protocol")] = "hysteria2"}) o:depends({ [_n("protocol")] = "hysteria2"})
end end
@@ -506,7 +533,7 @@ o.default = "0"
o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false }) o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false })
o:depends({ [_n("protocol")] = "tuic" }) o:depends({ [_n("protocol")] = "tuic" })
o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ [_n("protocol")] = "hysteria" })
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o:depends({ [_n("protocol")] = "naive" }) o:depends({ [_n("protocol")] = "naive" })
o = s:option(TextValue, _n("ech_config"), translate("ECH Config")) o = s:option(TextValue, _n("ech_config"), translate("ECH Config"))
@@ -818,10 +845,14 @@ local protocols = s.fields[_n("protocol")].keylist
if #protocols > 0 then if #protocols > 0 then
for i, v in ipairs(protocols) do for i, v in ipairs(protocols) do
if not v:find("^_") then if not v:find("^_") then
s.fields[_n("address")]:depends({ [_n("protocol")] = v }) local depends_condition = { [_n("protocol")] = v }
s.fields[_n("port")]:depends({ [_n("protocol")] = v }) if v == "hysteria2" then
s.fields[_n("domain_resolver")]:depends({ [_n("protocol")] = v }) depends_condition[_n("hysteria2_realms")] = false
s.fields[_n("domain_strategy")]:depends({ [_n("protocol")] = v }) end
s.fields[_n("address")]:depends(depends_condition)
s.fields[_n("port")]:depends(depends_condition)
s.fields[_n("domain_resolver")]:depends(depends_condition)
s.fields[_n("domain_strategy")]:depends(depends_condition)
end end
end end
end end
@@ -834,16 +865,21 @@ if not load_shunt_options then
if not (load_iface_options or load_urltest_options) then if not (load_iface_options or load_urltest_options) then
-- Special node cannot be use pre-proxy. -- Special node cannot be use pre-proxy.
o:value("1", translate("Preproxy Node")) o:value("1", translate("Preproxy Node"))
o:value("3", translate("Outbound Interface"))
end end
o:value("2", translate("Landing Node")) o:value("2", translate("Landing Node"))
o1 = s:option(ListValue, _n("preproxy_node"), translate("Preproxy Node"), translate("Only support a layer of proxy.")) o1 = s:option(ListValue, _n("preproxy_node"), translate("Preproxy Node"), translate("Only support a layer of proxy."))
o1:depends({ [_n("chain_proxy")] = "1" }) o1:depends({ [_n("chain_proxy")] = "1", [_n("hysteria2_realms")] = false })
o1.template = appname .. "/cbi/nodes_listvalue" o1.template = appname .. "/cbi/nodes_listvalue"
o1.group = {} o1.group = {}
o3 = s:option(Value, _n("outbound_iface"), translate("Outbound Interface"))
o3.default = "eth1"
o3:depends({ [_n("chain_proxy")] = "3" })
o2 = s:option(ListValue, _n("to_node"), translate("Landing Node"), translate("Only support a layer of proxy.")) o2 = s:option(ListValue, _n("to_node"), translate("Landing Node"), translate("Only support a layer of proxy."))
o2:depends({ [_n("chain_proxy")] = "2" }) o2:depends({ [_n("chain_proxy")] = "2", [_n("hysteria2_realms")] = false })
o2.template = appname .. "/cbi/nodes_listvalue" o2.template = appname .. "/cbi/nodes_listvalue"
o2.group = {} o2.group = {}
@@ -22,17 +22,38 @@ o = s:option(Flag, _n("custom"), translate("Use Custom Config"))
o = s:option(Value, _n("port"), translate("Listen Port")) o = s:option(Value, _n("port"), translate("Listen Port"))
o.datatype = "port" o.datatype = "port"
o.rmempty = false
o:depends({ [_n("custom")] = false }) o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("obfs"), translate("Obfs Password")) o = s:option(Flag, _n("realms"), translate("Realms"))
o.default = "0"
o.rewrite_option = o.option o.rewrite_option = o.option
o:depends({ [_n("custom")] = false }) o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o.rewrite_option = o.option
o:depends({ [_n("realms")] = "1" })
o = s:option(DynamicList, _n("realm_stun"), translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o.rewrite_option = o.option
o:depends({ [_n("realms")] = "1" })
o = s:option(Value, _n("auth_password"), translate("Auth Password")) o = s:option(Value, _n("auth_password"), translate("Auth Password"))
o.password = true o.password = true
o.rewrite_option = o.option o.rewrite_option = o.option
o:depends({ [_n("custom")] = false }) o:depends({ [_n("custom")] = false })
o = s:option(ListValue, _n("obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o.rewrite_option = o.option
o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("obfs_password"), translate("Obfs Password"))
o.rewrite_option = o.option
o:depends({ [_n("obfs_type")] = "salamander" })
o = s:option(Flag, _n("udp"), translate("UDP")) o = s:option(Flag, _n("udp"), translate("UDP"))
o.default = "1" o.default = "1"
o.rewrite_option = o.option o.rewrite_option = o.option
@@ -129,6 +129,14 @@ o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o.password = true o.password = true
o:depends({ [_n("protocol")] = "hysteria2"}) o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
o = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Client BBR Flow Control")) o = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Client BBR Flow Control"))
o.default = 0 o.default = 0
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ [_n("protocol")] = "hysteria2" })
@@ -139,14 +147,6 @@ o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwid
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps")) o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false }) o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false })
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
---- [[ TLS ]] ---- [[ TLS ]]
o = s:option(Flag, _n("tls"), translate("TLS")) o = s:option(Flag, _n("tls"), translate("TLS"))
o.default = 0 o.default = 0
@@ -58,6 +58,7 @@ o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("port"), translate("Listen Port")) o = s:option(Value, _n("port"), translate("Listen Port"))
o.datatype = "port" o.datatype = "port"
o.rmempty = false
o:depends({ [_n("custom")] = false }) o:depends({ [_n("custom")] = false })
o = s:option(Flag, _n("auth"), translate("Auth")) o = s:option(Flag, _n("auth"), translate("Auth"))
@@ -157,10 +158,29 @@ if singbox_tags:find("with_quic") then
end end
if singbox_tags:find("with_quic") then if singbox_tags:find("with_quic") then
o = s:option(Flag, _n("hysteria2_realms"), translate("Realms"))
o.default = "0"
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(Value, _n("hysteria2_realm_url"), translate("Realm URL"), translate("Example:") .. "realm://public@realm.hy2.io/your-realm-name")
o:depends({ [_n("hysteria2_realms")] = "1" })
o = s:option(DynamicList, _n("hysteria2_realm_stun"), translate("Realm STUN"))
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
o:depends({ [_n("hysteria2_realms")] = "1" })
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password")) o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o.password = true o.password = true
o:depends({ [_n("protocol")] = "hysteria2"}) o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
o = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Client BBR Flow Control"), translate("Commands the client to use the BBR flow control algorithm")) o = s:option(Flag, _n("hysteria2_ignore_client_bandwidth"), translate("Client BBR Flow Control"), translate("Commands the client to use the BBR flow control algorithm"))
o.default = 0 o.default = 0
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ [_n("protocol")] = "hysteria2" })
@@ -170,14 +190,6 @@ if singbox_tags:find("with_quic") then
o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps")) o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps"))
o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false }) o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_ignore_client_bandwidth")] = false })
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
o:value("", translate("Disable"))
o:value("salamander")
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_obfs_password"), translate("Obfs Password"))
o:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
end end
o = s:option(ListValue, _n("d_protocol"), translate("Destination protocol")) o = s:option(ListValue, _n("d_protocol"), translate("Destination protocol"))
@@ -324,7 +336,7 @@ o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false })
o:depends({ [_n("protocol")] = "naive" }) o:depends({ [_n("protocol")] = "naive" })
o:depends({ [_n("protocol")] = "hysteria" }) o:depends({ [_n("protocol")] = "hysteria" })
o:depends({ [_n("protocol")] = "tuic" }) o:depends({ [_n("protocol")] = "tuic" })
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ [_n("protocol")] = "hysteria2", [_n("hysteria2_realms")] = false })
o = s:option(TextValue, _n("ech_key"), translate("ECH Key")) o = s:option(TextValue, _n("ech_key"), translate("ECH Key"))
o.default = "" o.default = ""
@@ -31,7 +31,11 @@ local type_table = {}
for filename in fs.dir(types_dir) do for filename in fs.dir(types_dir) do
table.insert(type_table, filename) table.insert(type_table, filename)
end end
table.sort(type_table) table.sort(type_table, function(a, b)
if a == "socks.lua" then return true end
if b == "socks.lua" then return false end
return a < b
end)
for index, value in ipairs(type_table) do for index, value in ipairs(type_table) do
local p_func = loadfile(types_dir .. value) local p_func = loadfile(types_dir .. value)
+40 -5
View File
@@ -377,11 +377,13 @@ function is_special_node(e)
end end
function is_ip(val) function is_ip(val)
val = trim(val):lower()
local str = val:match("%[(.-)%]") or val local str = val:match("%[(.-)%]") or val
return datatypes.ipaddr(str) or false return datatypes.ipaddr(str) or false
end end
function is_ipv6(val) function is_ipv6(val)
val = trim(val):lower()
local str = val:match("%[(.-)%]") or val local str = val:match("%[(.-)%]") or val
return datatypes.ip6addr(str) or false return datatypes.ip6addr(str) or false
end end
@@ -497,9 +499,10 @@ function get_valid_nodes()
end end
end end
local port = e.port or e.hysteria_hop or e.hysteria2_hop local port = e.port or e.hysteria_hop or e.hysteria2_hop
if port and e.address then local is_realm = (e.type == "Hysteria2" or e.protocol == 'hysteria2') and e.hysteria2_realms or nil
if (port and e.address) or is_realm then
local address = e.address local address = e.address
if is_ip(address) or datatypes.hostname(address) then if is_ip(address) or datatypes.hostname(address) or is_realm then
if (e.type == "sing-box" or e.type == "Xray") and e.protocol then if (e.type == "sing-box" or e.type == "Xray") and e.protocol then
local protocol = e.protocol local protocol = e.protocol
if protocol == "vmess" then if protocol == "vmess" then
@@ -526,10 +529,13 @@ function get_valid_nodes()
type_name = type_name .. " " .. protocol type_name = type_name .. " " .. protocol
end end
if is_ipv6(address) then address = get_ipv6_full(address) end if is_ipv6(address) then address = get_ipv6_full(address) end
type_name = is_realm and type_name .. " Realm" or type_name
e["remark"] = trim("%s:[%s]" % {type_name, e.remarks}) e["remark"] = trim("%s:[%s]" % {type_name, e.remarks})
if show_node_info == "1" then if show_node_info == "1" then
port = port:gsub(":", "-") port = (port or ""):gsub(":", "-")
e["remark"] = trim("%s:[%s] %s:%s" % {type_name, e.remarks, address, port}) if not is_realm then
e["remark"] = trim("%s:[%s] %s:%s" % {type_name, e.remarks, address, port})
end
end end
e.node_type = "normal" e.node_type = "normal"
if not e.group or e.group == "" then if not e.group or e.group == "" then
@@ -619,6 +625,9 @@ function get_node_remarks(n)
end end
type_name = type_name .. " " .. protocol type_name = type_name .. " " .. protocol
end end
if (n.type == "Hysteria2" or n.protocol == 'hysteria2') and n.hysteria2_realms then
type_name = type_name .. " Realm"
end
remarks = trim("%s:[%s]" % {type_name, n.remarks}) remarks = trim("%s:[%s]" % {type_name, n.remarks})
end end
end end
@@ -1204,7 +1213,7 @@ function to_move(app_name,file)
} }
end end
local flag = sys.call('pgrep -af "passwall2/.*'.. app_name ..'" >/dev/null') local flag = sys.call('busybox pgrep -af "passwall2/.*'.. app_name ..'" >/dev/null')
if flag == 0 then if flag == 0 then
sys.call("/etc/init.d/passwall2 stop") sys.call("/etc/init.d/passwall2 stop")
end end
@@ -1588,3 +1597,29 @@ function get_dnsmasq_server_domain()
end end
return dnsmasq_server_t return dnsmasq_server_t
end end
function parse_realm_uri(uri)
if type(uri) ~= "string" then return nil end
-- realm://token@server/realm_id?query
local token, server_url, realm_id, query = trim(uri):match("^realm://([^@]+)@([^/]+)/([^?]*)%??(.*)$")
if not token or not server_url or not realm_id then return nil end
realm_id = realm_id:gsub("/+$", "")
local realm = {
token = token,
server_url = server_url,
realm_id = realm_id
}
-- 解析 query 中的 stun=
if query and query ~= "" then
local stun_servers = {}
for key, value in query:gmatch("([^&=?]+)=([^&]+)") do
if key == "stun" and value ~= "" then
stun_servers[#stun_servers + 1] = value
end
end
if #stun_servers > 0 then
realm.stun_servers = stun_servers
end
end
return realm
end
@@ -5,15 +5,24 @@ local jsonc = api.jsonc
function gen_config_server(node) function gen_config_server(node)
local config = { local config = {
listen = ":" .. node.port, listen = (function()
if node.hysteria2_realms and node.hysteria2_realm_url then
local url = node.hysteria2_realm_url:gsub("/+$", "")
if node.port then
url = url .. (url:find("?") and "&lport=" or "?lport=") .. node.port
end
return url
end
return ":" .. (node.port or "0")
end)(),
tls = { tls = {
cert = node.tls_certificateFile, cert = node.tls_certificateFile,
key = node.tls_keyFile, key = node.tls_keyFile,
}, },
obfs = (node.hysteria2_obfs) and { obfs = (node.hysteria2_obfs_type and node.hysteria2_obfs_password) and {
type = "salamander", type = "salamander",
salamander = { salamander = {
password = node.hysteria2_obfs password = node.hysteria2_obfs_password
} }
} or nil, } or nil,
auth = { auth = {
@@ -26,6 +35,9 @@ function gen_config_server(node)
} or nil, } or nil,
ignoreClientBandwidth = (node.hysteria2_ignoreClientBandwidth == "1") and true or false, ignoreClientBandwidth = (node.hysteria2_ignoreClientBandwidth == "1") and true or false,
disableUDP = (node.hysteria2_udp == "0") and true or false, disableUDP = (node.hysteria2_udp == "0") and true or false,
realm = (node.hysteria2_realms and node.hysteria2_realm_stun) and {
stunServers = node.hysteria2_realm_stun
} or nil
} }
return config return config
end end
@@ -51,14 +63,19 @@ function gen_config(var)
if api.is_ipv6(server_host) then if api.is_ipv6(server_host) then
server_host = api.get_ipv6_full(server_host) server_host = api.get_ipv6_full(server_host)
end end
local server = server_host .. ":" .. server_port
if (node.hysteria2_hop) then local server = server_host .. ":" .. ((server_port or "") .. "," .. (node.hysteria2_hop or "")):gsub("^[%s,]+", ""):gsub("[%s,]+$", ""):gsub(":", "-")
server = server .. "," .. string.gsub(node.hysteria2_hop, ":", "-")
end
local config = { local config = {
server = server, server = (function()
if node.hysteria2_realms and node.hysteria2_realm_url then
return node.hysteria2_realm_url:gsub("/+$", "")
end
return server
end)(),
realm = (node.hysteria2_realms and node.hysteria2_realm_stun) and {
stunServers = node.hysteria2_realm_stun
} or nil,
transport = { transport = {
type = "udp", type = "udp",
udp = node.hysteria2_hop and (function() udp = node.hysteria2_hop and (function()
@@ -83,10 +100,10 @@ function gen_config(var)
return udp return udp
end)() or nil end)() or nil
}, },
obfs = (node.hysteria2_obfs) and { obfs = (node.hysteria2_obfs_type and node.hysteria2_obfs_password) and {
type = "salamander", type = "salamander",
salamander = { salamander = {
password = node.hysteria2_obfs password = node.hysteria2_obfs_password
} }
} or nil, } or nil,
auth = node.hysteria2_auth_password, auth = node.hysteria2_auth_password,
@@ -134,8 +134,8 @@ function gen_outbound(flag, node, tag, proxy_table)
local run_socks_instance = true 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 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 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 run_socks_instance = proxy_table.run_socks_instance
end end
@@ -250,7 +250,7 @@ function gen_outbound(flag, node, tag, proxy_table)
enabled = true, enabled = true,
disable_sni = (node.tls_disable_sni == "1") and true or false, -- Do not send the server name in ClientHello. disable_sni = (node.tls_disable_sni == "1") and true or false, -- Do not send the server name in ClientHello.
server_name = node.tls_serverName, -- Used to verify the hostname on the returned certificate, unless the settings are insecure. It is also included in ClientHello to support virtual hosts, unless it is an IP address. server_name = node.tls_serverName, -- Used to verify the hostname on the returned certificate, unless the settings are insecure. It is also included in ClientHello to support virtual hosts, unless it is an IP address.
insecure = (node.tls_allowInsecure == "1") and true or false, -- Accepts any server certificate. insecure = node.tls_allowInsecure == "1" or (node.tls_pinSHA256 and node.tls_pinSHA256 ~= ""), -- Accepts any server certificate. (Also compatible with xray's pinnedPeerCertSha256)
alpn = alpn, -- A list of supported application layer protocols, arranged in order of priority. If both peers support ALPN, the protocol selected will be one of these protocols; otherwise, the connection will fail. alpn = alpn, -- A list of supported application layer protocols, arranged in order of priority. If both peers support ALPN, the protocol selected will be one of these protocols; otherwise, the connection will fail.
--max_version = "1.3", --max_version = "1.3",
fragment = fragment, fragment = fragment,
@@ -506,6 +506,7 @@ function gen_outbound(flag, node, tag, proxy_table)
table.insert(server_ports, range) table.insert(server_ports, range)
end end
end end
result.server_port = nil
end end
protocol_table = { protocol_table = {
server_ports = next(server_ports) and server_ports or nil, server_ports = next(server_ports) and server_ports or nil,
@@ -519,9 +520,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), recv_window_conn = tonumber(node.hysteria_recv_window_conn), --1.14 将变更为 stream_receive_window
recv_window = tonumber(node.hysteria_recv_window), recv_window = tonumber(node.hysteria_recv_window), --1.14 将变更为 connection_receive_window
disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false, disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false, --1.14 将变更为 disable_path_mtu_discovery
tls = tls tls = tls
} }
end end
@@ -545,13 +546,12 @@ function gen_outbound(flag, node, tag, proxy_table)
heartbeat = (tonumber(node.tuic_heartbeat) or 3) .. "s", heartbeat = (tonumber(node.tuic_heartbeat) or 3) .. "s",
tls = tls tls = tls
} }
if node.tuic_alpn and node.tuic_alpn ~= "default" then node.tuic_alpn = (node.tuic_alpn and node.tuic_alpn ~= "default") and node.tuic_alpn or "h3"
local alpn = {} local alpn = {}
string.gsub(node.tuic_alpn, '[^,]+', function(w) string.gsub(node.tuic_alpn, '[^,]+', function(w)
table.insert(alpn, w) table.insert(alpn, w)
end) end)
if #alpn > 0 then protocol_table.tls.alpn = alpn end if #alpn > 0 then protocol_table.tls.alpn = alpn end
end
end end
if node.protocol == "hysteria2" then if node.protocol == "hysteria2" then
@@ -563,6 +563,10 @@ function gen_outbound(flag, node, tag, proxy_table)
table.insert(server_ports, range) table.insert(server_ports, range)
end end
end end
result.server_port = nil
end
if node.hysteria2_realms then
server_ports = {}
end end
local interval, interval_max local interval, interval_max
if next(server_ports) then if next(server_ports) then
@@ -595,7 +599,29 @@ function gen_outbound(flag, node, tag, proxy_table)
password = node.hysteria2_obfs_password password = node.hysteria2_obfs_password
} or nil, } or nil,
password = node.hysteria2_auth_password or nil, password = node.hysteria2_auth_password or nil,
tls = tls idle_timeout = (function(t)
if not version_ge_1_14_0 then return nil end
t = tonumber(tostring(t or "30"):match("^%d+"))
return (t and t >= 4 and t <= 120) and t or 30
end)(node.hysteria2_idle_timeout),
keep_alive_period = (function(t)
if not version_ge_1_14_0 then return nil end
t = tonumber(tostring(t or "0"):match("^%d+"))
return (t and t >= 2 and t <= 60) and t or nil
end)(node.hysteria2_keep_alive_period),
disable_path_mtu_discovery = version_ge_1_14_0 and (tonumber(node.hysteria2_disable_mtu_discovery) == 1) or nil,
tls = tls,
realm = node.hysteria2_realms and (function()
result.server = nil
result.server_port = nil
local realm = api.parse_realm_uri(node.hysteria2_realm_url)
if realm then
realm.server_url = realm.server_url and "https://" .. realm.server_url or nil
realm.stun_servers = realm.stun_servers or node.hysteria2_realm_stun
return realm
end
return nil
end)() or nil
} }
end end
@@ -881,10 +907,10 @@ function gen_config_server(node)
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 = node.hysteria_recv_window_conn and tonumber(node.hysteria_recv_window_conn) or nil, recv_window_conn = node.hysteria_recv_window_conn and tonumber(node.hysteria_recv_window_conn) or nil, --1.14 to stream_receive_window
recv_window_client = node.hysteria_recv_window_client and tonumber(node.hysteria_recv_window_client) or nil, recv_window_client = node.hysteria_recv_window_client and tonumber(node.hysteria_recv_window_client) or nil, --1.14 to connection_receive_window
max_conn_client = node.hysteria_max_conn_client and tonumber(node.hysteria_max_conn_client) or nil, max_conn_client = node.hysteria_max_conn_client and tonumber(node.hysteria_max_conn_client) or nil, --1.14 to max_concurrent_streams
disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false, disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false, --1.14 to disable_path_mtu_discover
tls = tls tls = tls
} }
end end
@@ -928,7 +954,17 @@ function gen_config_server(node)
} }
}, },
ignore_client_bandwidth = (node.hysteria2_ignore_client_bandwidth == "1") and true or false, ignore_client_bandwidth = (node.hysteria2_ignore_client_bandwidth == "1") and true or false,
tls = tls tls = tls,
realm = node.hysteria2_realms and (function()
local realm = api.parse_realm_uri(node.hysteria2_realm_url)
if realm then
realm.server_url = realm.server_url and "https://" .. realm.server_url or nil
realm.stun_servers = realm.stun_servers or node.hysteria2_realm_stun
realm.stun_domain_resolver = "direct"
return realm
end
return nil
end)() or nil
} }
end end
@@ -1312,7 +1348,6 @@ function gen_config(var)
else else
ut_nodes = _node.urltest_node ut_nodes = _node.urltest_node
end end
if #ut_nodes == 0 then return nil end
local valid_nodes = {} local valid_nodes = {}
for i = 1, #(ut_nodes or {}) do for i = 1, #(ut_nodes or {}) do
local ut_node_id = ut_nodes[i] local ut_node_id = ut_nodes[i]
@@ -1444,6 +1479,10 @@ function gen_config(var)
end end
end end
end end
if node.chain_proxy == "3" and node.outbound_iface then
outbound.detour = nil
outbound.bind_interface = node.outbound_iface
end
return default_outTag, last_insert_outbound return default_outTag, last_insert_outbound
end end
@@ -1775,6 +1814,10 @@ function gen_config(var)
tag = "local" tag = "local"
}) })
route.default_domain_resolver = {
server = "local"
}
if direct_dns_udp_server then if direct_dns_udp_server then
table.insert(dns.servers, { table.insert(dns.servers, {
tag = "direct", tag = "direct",
@@ -2051,7 +2094,7 @@ function gen_config(var)
string.gsub(direct_ipset, '[^' .. "," .. ']+', function(w) string.gsub(direct_ipset, '[^' .. "," .. ']+', function(w)
sys.call("ipset -q -F " .. w) sys.call("ipset -q -F " .. w)
end) end)
local ipset_prefix_name = "passwall2_" .. node_id .. "_" local ipset_prefix_name = "psw2_" .. node_id .. "_"
local ipset_list = sys.exec("ipset list | grep 'Name: ' | grep '" .. ipset_prefix_name .. "' | awk '{print $2}'") local ipset_list = sys.exec("ipset list | grep 'Name: ' | grep '" .. ipset_prefix_name .. "' | awk '{print $2}'")
string.gsub(ipset_list, '[^' .. "\r\n" .. ']+', function(w) string.gsub(ipset_list, '[^' .. "\r\n" .. ']+', function(w)
sys.call("ipset -q -F " .. w) sys.call("ipset -q -F " .. w)
@@ -2070,7 +2113,7 @@ function gen_config(var)
end) end)
local family = "inet" local family = "inet"
local table_name = "passwall2" local table_name = "passwall2"
local nftset_prefix_name = "passwall2_" .. node_id .. "_" local nftset_prefix_name = "psw2_" .. node_id .. "_"
local nftset_list = sys.exec("nft -a list sets | grep -E '" .. nftset_prefix_name .. "' | awk -F 'set ' '{print $2}' | awk '{print $1}'") local nftset_list = sys.exec("nft -a list sets | grep -E '" .. nftset_prefix_name .. "' | awk -F 'set ' '{print $2}' | awk '{print $1}'")
string.gsub(nftset_list, '[^' .. "\r\n" .. ']+', function(w) string.gsub(nftset_list, '[^' .. "\r\n" .. ']+', function(w)
sys.call(string.format("nft flush set %s %s %s 2>/dev/null", family, table_name, w)) sys.call(string.format("nft flush set %s %s %s 2>/dev/null", family, table_name, w))
@@ -2126,19 +2169,15 @@ function gen_config(var)
type = "direct", type = "direct",
tag = "direct", tag = "direct",
routing_mark = 255, routing_mark = 255,
domain_resolver = {
server = "local",
strategy = "prefer_ipv6"
}
}) })
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 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 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 value.server then if not value.detour and not value.bind_interface and value.server then
value.detour = "direct" value.detour = "direct"
end end
if value.server and not api.datatypes.hostname(value.server) then if (value.server and not api.datatypes.hostname(value.server)) and not value.realm then
value.domain_resolver = nil value.domain_resolver = nil
end end
for k, v in pairs(config.outbounds[index]) do for k, v in pairs(config.outbounds[index]) do
@@ -2290,10 +2329,6 @@ function gen_front_dns_config(var)
type = "direct", type = "direct",
tag = "direct", tag = "direct",
routing_mark = 255, routing_mark = 255,
domain_resolver = {
server = "direct",
strategy = direct_strategy
}
}) })
local direct_dns_shunt = uci:get(appname, "@global[0]", "direct_dns_shunt") or "" local direct_dns_shunt = uci:get(appname, "@global[0]", "direct_dns_shunt") or ""
@@ -2363,6 +2398,10 @@ function gen_front_dns_config(var)
domain = node_domain domain = node_domain
}) })
end end
route.default_domain_resolver = {
server = "direct",
strategy = direct_strategy
}
end end
if default_dns_udp_server then if default_dns_udp_server then
@@ -794,6 +794,9 @@ 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
@@ -1049,10 +1052,10 @@ function gen_config(var)
if #valid_nodes == 0 then return nil end if #valid_nodes == 0 then return nil end
-- fallback node -- fallback node
local fallback_node_tag = nil
local fallback_node_id = _node.fallback_node local fallback_node_id = _node.fallback_node
if not fallback_node_id or fallback_node_id == "" then fallback_node_id = nil end fallback_node_id = (fallback_node_id and fallback_node_id ~= "") and fallback_node_id or nil
if fallback_node_id then local fallback_node_tag = (fallback_node_id == "_direct") and "direct" or "blackhole"
if fallback_node_id and fallback_node_id ~= "_direct" then
local is_new_node = true local is_new_node = true
for _, outbound in ipairs(outbounds) do for _, outbound in ipairs(outbounds) do
if string.sub(outbound.tag, 1, #fallback_node_id) == fallback_node_id then if string.sub(outbound.tag, 1, #fallback_node_id) == fallback_node_id then
@@ -1082,7 +1085,13 @@ function gen_config(var)
type = _node.balancingStrategy, type = _node.balancingStrategy,
settings = { settings = {
expected = _node.expected and tonumber(_node.expected) and tonumber(_node.expected) or 2, expected = _node.expected and tonumber(_node.expected) and tonumber(_node.expected) or 2,
maxRTT = "1s" maxRTT = "1s",
tolerance = (function(t)
t = tonumber(t) or 0
if t < 1 then return nil end
if t > 100 then t = 100 end
return t / 100
end)(_node.tolerance)
} }
} }
else else
@@ -1264,9 +1273,9 @@ function gen_config(var)
interface = node.iface interface = node.iface
} }
}, },
settings = { settings = (api.compare_versions(xray_version, ">", "26.4.25")) and { -- Todo: Remove version check
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
@@ -1763,15 +1772,22 @@ function gen_config(var)
end end
end end
end end
local dns_block_mode = "host" local dns_block = true -- If you do not want the blackhole rule to use DNS routing, please change it to `nil` or do not assign a value.
dns_block_mode = "" if dns_outboundTag == "blackhole" then
if dns_block_mode == "host" and dns_outboundTag == "blackhole" then if dns_block then
for d_i, d_k in ipairs(value.domain) do local dns_block_mode = "Hosts" -- use the Hosts mode to block.
dns.hosts[d_k] = "0.0.0.0" if dns_block_mode == "Hosts" then
for d_i, d_k in ipairs(value.domain) do
dns.hosts[d_k] = "0.0.0.0"
end
dns_server = nil
end
else
dns_server = nil
end end
dns_server = nil
end end
if dns_server then if dns_server then
dns_server.finalQuery = true
dns_server.domains = value.domain dns_server.domains = value.domain
if value.shunt_rule_name then if value.shunt_rule_name then
dns_server.tag = "dns-in-" .. value.shunt_rule_name dns_server.tag = "dns-in-" .. value.shunt_rule_name
@@ -1806,7 +1822,7 @@ function gen_config(var)
string.gsub(direct_ipset, '[^' .. "," .. ']+', function(w) string.gsub(direct_ipset, '[^' .. "," .. ']+', function(w)
sys.call("ipset -q -F " .. w) sys.call("ipset -q -F " .. w)
end) end)
local ipset_prefix_name = "passwall2_" .. node_id .. "_" local ipset_prefix_name = "psw2_" .. node_id .. "_"
local ipset_list = sys.exec("ipset list | grep 'Name: ' | grep '" .. ipset_prefix_name .. "' | awk '{print $2}'") local ipset_list = sys.exec("ipset list | grep 'Name: ' | grep '" .. ipset_prefix_name .. "' | awk '{print $2}'")
string.gsub(ipset_list, '[^' .. "\r\n" .. ']+', function(w) string.gsub(ipset_list, '[^' .. "\r\n" .. ']+', function(w)
sys.call("ipset -q -F " .. w) sys.call("ipset -q -F " .. w)
@@ -1825,7 +1841,7 @@ function gen_config(var)
end) end)
local family = "inet" local family = "inet"
local table_name = "passwall2" local table_name = "passwall2"
local nftset_prefix_name = "passwall2_" .. node_id .. "_" local nftset_prefix_name = "psw2_" .. node_id .. "_"
local nftset_list = sys.exec("nft -a list sets | grep -E '" .. nftset_prefix_name .. "' | awk -F 'set ' '{print $2}' | awk '{print $1}'") local nftset_list = sys.exec("nft -a list sets | grep -E '" .. nftset_prefix_name .. "' | awk -F 'set ' '{print $2}' | awk '{print $1}'")
string.gsub(nftset_list, '[^' .. "\r\n" .. ']+', function(w) string.gsub(nftset_list, '[^' .. "\r\n" .. ']+', function(w)
sys.call(string.format("nft flush set %s %s %s 2>/dev/null", family, table_name, w)) sys.call(string.format("nft flush set %s %s %s 2>/dev/null", family, table_name, w))
@@ -2080,7 +2096,12 @@ function gen_proto_config(var)
end end
table.insert(outbounds, { table.insert(outbounds, {
protocol = "freedom", tag = "direct", settings = {finalRules = {{ action = "allow" }}}, sockopt = {mark = 255} protocol = "freedom",
tag = "direct",
settings = (api.compare_versions(xray_version, ">", "26.4.25")) and { -- Todo: Remove version check
finalRules = {{ action = "allow" }}
} or nil,
sockopt = {mark = 255}
}) })
local config = { local config = {
@@ -35,21 +35,47 @@ local appname = api.appname
</style> </style>
<script type="text/javascript"> <script type="text/javascript">
//<![CDATA[ //<![CDATA[
var shunt_list = JSON.parse('<%=self.shunt_list%>'); const shunt_list = JSON.parse('<%=self.shunt_list%>');
function go() { function to_edit_node(btn) {
var _status = document.getElementsByClassName('_status'); if (!btn) return;
for (var i = 0; i < _status.length; i++) { const idReg = /^cbid\..*\..*node$/;
var id = _status[i].getAttribute("socks_id"); let hidden_select = null;
const container = btn.closest('#cbi-<%=appname%>-global') || btn.closest('#cbi-<%=appname%>-socks');
if (!container) return null;
const selects = container.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("_default") === 0 || node_select_value.indexOf("_direct") === 0 || node_select_value.indexOf("_blackhole") === 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.substring("Socks_".length);
}
location.href = to_url;
}
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
async function go() {
const _status = document.getElementsByClassName('_status');
for (let i = 0; i < _status.length; i++) {
const id = _status[i].getAttribute("socks_id");
XHR.get('<%=api.url("socks_status")%>', { XHR.get('<%=api.url("socks_status")%>', {
index: i, index: i,
id: id id: id
}, },
function(x, result) { function(x, result) {
var index = result.index; const index = result.index;
var div = ''; let div = '';
var div1 = '<font style="font-weight:bold;" color="green">✓</font>&nbsp'; const div1 = '<font style="font-weight:bold;" color="green">✓</font>&nbsp';
var div2 = '<font style="font-weight:bold;" color="red">X</font>&nbsp'; const div2 = '<font style="font-weight:bold;" color="red">X</font>&nbsp';
if (result.socks_status) { if (result.socks_status) {
div += div1; div += div1;
@@ -68,82 +94,111 @@ local appname = api.appname
); );
} }
let global_id = null;
var global_id = null; const global = document.getElementById("cbi-<%=appname%>-global");
var global = document.getElementById("cbi-<%=appname%>-global");
if (global) { if (global) {
var node = global.getElementsByClassName("cbi-section-node")[0]; // Add an "Edit" placeholder to the routing rule node in advance to ensure left alignment of the node list.
var node_id = node.getAttribute("id"); document.querySelectorAll('.td[data-widget="nodes_listvalue"][data-name="_node"] > div').forEach(container => {
global_id = node_id; if (!container.querySelector('.shunt-node-placeholder')) {
var all_node = node.querySelectorAll("[id]"); const placeholder = document.createElement('div');
var reg1 = /^cbid\..*node\.main$/; placeholder.className = 'shunt-node-placeholder';
placeholder.style.cssText = 'display:inline-flex; width:40px; flex-shrink:0;';
container.appendChild(placeholder);
}
});
for (var i = 0; i < all_node.length; i++) { await delay(100);
var el = all_node[i];
const node = global.getElementsByClassName("cbi-section-node")[0];
const node_id = node.getAttribute("id");
global_id = node_id;
const all_node = node.querySelectorAll("[id]");
const reg1 = /^cbid\..*node\.main$/;
//const reg1 = /^cbid\..*\.(node)\.main$/;
for (let i = 0; i < all_node.length; i++) {
const el = all_node[i];
if (!reg1.test(el.id)) continue; if (!reg1.test(el.id)) continue;
var node_select = el; const node_select = el;
if (!node_select) continue; if (!node_select) continue;
var cbid = el.id.replace(/\.main$/, ""); const cbid = el.id.replace(/\.main$/, "");
var hidden_select = document.getElementById(cbid); const hidden_select = document.getElementById(cbid);
var node_select_value = hidden_select ? hidden_select.options[0].value : ""; const node_select_value = hidden_select?.options[0]?.value;
if (!node_select_value || node_select_value.indexOf("_default") === 0 || node_select_value.indexOf("_direct") === 0 || node_select_value.indexOf("_blackhole") === 0) { if (!node_select_value || node_select_value.indexOf("_default") === 0 || node_select_value.indexOf("_direct") === 0 || node_select_value.indexOf("_blackhole") === 0) {
continue; continue;
} }
var to_url = '<%=api.url("node_config")%>/' + node_select_value; let html = '<a href="#" onclick="return to_edit_node(this);"><%:Edit%></a>';
if (node_select_value.indexOf("Socks_") === 0) {
to_url = '<%=api.url("socks_config")%>/' + node_select_value.substring("Socks_".length);
}
var html = '<a href="#" onclick="location.href=\'' + to_url + '\'"><%:Edit%></a>';
var m = cbid.match(/\.node$/); const m = cbid.match(/\.node$/);
if (m) { if (m) {
html += '<a href="#" onclick="window.open(\'' + '<%=api.url("get_redir_log")%>?id=default&name=' + 'global' + '\', \'_blank\')"><%:Log%></a>'; html += '<a href="#" onclick="window.open(\'' + '<%=api.url("get_redir_log")%>?id=default&name=' + 'global' + '\', \'_blank\')"><%:Log%></a>';
} }
node_select.insertAdjacentHTML("beforeend", html = '<div class="node-actions" style="display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; margin-left:4px;">' + html + '</div>'
'<div class="node-actions" style="display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; margin-left:4px;">'
+ html + '</div>' const placeholder = node_select.parentElement.querySelector('.shunt-node-placeholder');
); if (placeholder) {
placeholder.innerHTML = html;
} else {
node_select.insertAdjacentHTML("beforeend", html);
}
} }
} }
var socks = document.getElementById("cbi-<%=appname%>-socks"); const socks = document.getElementById("cbi-<%=appname%>-socks");
if (socks) { if (socks) {
var socks_enabled_dom = document.getElementById(global_id + "-socks_enabled"); const socks_enabled_dom = document.getElementById(global_id + "-socks_enabled");
socks_enabled_dom.parentNode.removeChild(socks_enabled_dom); const descr = socks.getElementsByClassName("cbi-section-descr")[0];
var descr = socks.getElementsByClassName("cbi-section-descr")[0]; descr.replaceWith(socks_enabled_dom);
descr.outerHTML = socks_enabled_dom.outerHTML; const rows = socks.getElementsByClassName("cbi-section-table-row");
rows = socks.getElementsByClassName("cbi-section-table-row"); for (let i = 0; i < rows.length; i++) {
for (var i = 0; i < rows.length; i++) {
try { try {
var row = rows[i]; const row = rows[i];
var id = row.id; const id = row.id;
if (!id) continue; if (!id) continue;
var dom_id = id + "-node"; let dom_id = id + "-node";
var cbid = dom_id.replace("cbi-", "cbid-").replace(new RegExp("-", 'g'), "."); const cbid = dom_id.replace("cbi-", "cbid-").replace(new RegExp("-", 'g'), ".");
dom_id = cbid + ".main"; dom_id = cbid + ".main";
var node_select = document.getElementById(dom_id); const node_select = document.getElementById(dom_id);
if (!node_select) continue; if (!node_select) continue;
var hidden_select = document.getElementById(cbid);; let html = '<a href="#" onclick="return to_edit_node(this);"><%:Edit%></a>';
var node_select_value = hidden_select ? hidden_select.options[0].value : ""; html += '<a href="#" onclick="window.open(\'' + '<%=api.url("get_socks_log")%>?name=' + id.replace("cbi-<%=appname%>-", "") + '\', \'_blank\')"><%:Log%></a>';
if (node_select_value && node_select_value != "") {
var to_url = '<%=api.url("node_config")%>/' + node_select_value;
var html = '<a href="#" onclick="location.href=\'' + to_url + '\'"><%:Edit%></a>';
html += '<a href="#" onclick="window.open(\'' + '<%=api.url("get_socks_log")%>?name=' + id.replace("cbi-<%=appname%>-", "") + '\', \'_blank\')"><%:Log%></a>';
node_select.insertAdjacentHTML("afterend", node_select.insertAdjacentHTML("afterend",
'<div class="node-actions" style="display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; margin-left:4px;">' '<div class="node-actions" style="display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; margin-left:4px;">'
+ html + '</div>' + html + '</div>'
); );
}
} catch(err) { } catch(err) {
} }
} }
} }
} }
(function () {
let lastCount = 0;
let stableTimes = 0;
const STABLE_THRESHOLD = 5;
function waitStable() {
const list = document.querySelectorAll('[id^="cbid.<%=appname%>."][id$="node.main"]');
const count = list.length;
if (count === lastCount && count > 0) {
stableTimes++;
} else {
stableTimes = 0;
lastCount = count;
}
if (stableTimes >= STABLE_THRESHOLD) {
go();
return;
}
requestAnimationFrame(waitStable);
}
waitStable();
})();
document.addEventListener("DOMContentLoaded", () => setTimeout(() => { document.addEventListener("DOMContentLoaded", () => setTimeout(() => {
const hiddenSelect = document.getElementById("cbid.<%=appname%>.<%=self.global_cfgid%>.node"); const hiddenSelect = document.getElementById("cbid.<%=appname%>.<%=self.global_cfgid%>.node");
let o_val = hiddenSelect.value let o_val = hiddenSelect.value
@@ -184,8 +239,6 @@ local appname = api.appname
shunt_taboption.appendChild(shunt_option_list); shunt_taboption.appendChild(shunt_option_list);
} }
} }
go();
}, 100)); }, 100));
//setTimeout("go()", 1000);
//]]> //]]>
</script> </script>
@@ -602,10 +602,10 @@ local current_node = map:get(section)
if (v_type === "Hysteria2") { if (v_type === "Hysteria2") {
v_password = opt.get("hysteria2_auth_password"); v_password = opt.get("hysteria2_auth_password");
var dom_obfs = opt.get("hysteria2_obfs"); var dom_obfs = opt.get("hysteria2_obfs_password");
if (dom_obfs && dom_obfs.value != "") { if (dom_obfs && dom_obfs.value != "") {
params += "&obfs=" + "salamander"; params += "&obfs=" + "salamander";
params += opt.query("obfs-password", "hysteria2_obfs"); params += opt.query("obfs-password", "hysteria2_obfs_password");
} }
params += opt.query("mport", "hysteria2_hop"); params += opt.query("mport", "hysteria2_hop");
} else { } else {
@@ -1621,7 +1621,8 @@ local current_node = map:get(section)
dom_prefix = "hysteria2_" dom_prefix = "hysteria2_"
opt.set(dom_prefix + 'auth_password', decodeURIComponent(password)); opt.set(dom_prefix + 'auth_password', decodeURIComponent(password));
if (queryParam["obfs-password"] || queryParam["obfs_password"]) { if (queryParam["obfs-password"] || queryParam["obfs_password"]) {
opt.set(dom_prefix + 'obfs', queryParam["obfs-password"] || queryParam["obfs_password"]); opt.set(dom_prefix + 'obfs_type', "salamander");
opt.set(dom_prefix + 'obfs_password', queryParam["obfs-password"] || queryParam["obfs_password"]);
} }
opt.set(dom_prefix + 'hop', queryParam.mport || ""); opt.set(dom_prefix + 'hop', queryParam.mport || "");
} }
@@ -1678,7 +1679,7 @@ local current_node = map:get(section)
} }
opt.set(dom_prefix + 'tuic_congestion_control', queryParam.congestion_control || 'cubic'); opt.set(dom_prefix + 'tuic_congestion_control', queryParam.congestion_control || 'cubic');
opt.set(dom_prefix + 'tuic_udp_relay_mode', queryParam.udp_relay_mode || 'native'); opt.set(dom_prefix + 'tuic_udp_relay_mode', queryParam.udp_relay_mode || 'native');
opt.set(dom_prefix + 'tuic_alpn', queryParam.alpn || 'default'); opt.set(dom_prefix + 'tuic_alpn', queryParam.alpn || 'h3');
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || ''); opt.set(dom_prefix + 'tls_serverName', queryParam.sni || '');
opt.set(dom_prefix + 'tls_disable_sni', queryParam.disable_sni === "1"); opt.set(dom_prefix + 'tls_disable_sni', queryParam.disable_sni === "1");
opt.set( opt.set(
@@ -875,6 +875,8 @@ table td, .table .td {
p = "URLTest"; p = "URLTest";
} else if (protocol == "_shunt") { } else if (protocol == "_shunt") {
p = "<%:Shunt%>"; p = "<%:Shunt%>";
} else if (protocol == "_iface") {
p = "<%:Interface%>";
} else if (protocol == "vmess") { } else if (protocol == "vmess") {
p = "VMess"; p = "VMess";
} else if (protocol == "vless") { } else if (protocol == "vless") {
@@ -911,7 +913,11 @@ table td, .table .td {
} else { } else {
port_s = o["hysteria_hop"] || o["hysteria2_hop"]; port_s = o["hysteria_hop"] || o["hysteria2_hop"];
} }
str += type + ":" + remarks; if ((o.type === "Hysteria2" || o.protocol === 'hysteria2') && o.hysteria2_realms) {
str += type + " Realm:" + remarks;
} else {
str += type + ":" + remarks;
}
return str; return str;
} }
@@ -953,7 +959,7 @@ table td, .table .td {
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;
if (auto_detection_time != "icmp" && o["address"] && o["port"]) { if (auto_detection_time != "icmp" && o["address"]) {
innerHTML = innerHTML.split("{{ping}}").join('<span class="ping"><a href="javascript:void(0)" onclick="javascript:ping_node(\'{{id}}\', this, \'icmp\')"><%:Test%></a></span>'); innerHTML = innerHTML.split("{{ping}}").join('<span class="ping"><a href="javascript:void(0)" onclick="javascript:ping_node(\'{{id}}\', this, \'icmp\')"><%:Test%></a></span>');
} else { } else {
innerHTML = innerHTML.split("{{ping}}").join('<span class="ping_value" cbiid="{{id}}"><a href="javascript:void(0)" style="color:inherit">---</a></span>'); innerHTML = innerHTML.split("{{ping}}").join('<span class="ping_value" cbiid="{{id}}"><a href="javascript:void(0)" style="color:inherit">---</a></span>');
@@ -972,11 +978,13 @@ table td, .table .td {
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") {
if (o["address"] && o["port"]) { let _port = o["port"] || o["hysteria_hop"] || o["hysteria2_hop"];
if (_port) _port = _port.replace(/:/g, '-');
if (o["address"] && _port) {
let _address = o["address"] let _address = o["address"]
if (o["full_address"]) if (o["full_address"])
_address = o["full_address"] _address = o["full_address"]
node_remarks += "<br>" + _address + ":" + o["port"] node_remarks += "<br>" + _address + ":" + _port
} }
} }
innerHTML = innerHTML.split("{{remarks}}").join(node_remarks); innerHTML = innerHTML.split("{{remarks}}").join(node_remarks);
+11 -2
View File
@@ -46,8 +46,8 @@ msgstr "لیست قوانین"
msgid "Access control" msgid "Access control"
msgstr "کنترل دسترسی" msgstr "کنترل دسترسی"
msgid "Watch Logs" msgid "Runtime Logs"
msgstr "مشاهده گزارش‌ها" msgstr "گزارش‌های زمان اجرا"
msgid "Node Config" msgid "Node Config"
msgstr "پیکربندی گره" msgstr "پیکربندی گره"
@@ -437,6 +437,12 @@ msgstr "تعداد گره‌های ترجیحی"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them." msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
msgstr "توزیع‌کننده بار تعداد بهینه‌ای از گره‌ها را انتخاب می‌کند و ترافیک به طور تصادفی بین آن‌ها توزیع می‌شود." msgstr "توزیع‌کننده بار تعداد بهینه‌ای از گره‌ها را انتخاب می‌کند و ترافیک به طور تصادفی بین آن‌ها توزیع می‌شود."
msgid "Failure Tolerance (%)"
msgstr "تحمل شکست (%)"
msgid "The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate."
msgstr "حداکثر نرخ شکست قابل قبول در تست سرعت. برای مثال، عدد ۱ به معنای مجاز بودن نرخ شکست ۱٪ است.
msgid "Shunt" msgid "Shunt"
msgstr "شنت (Shunt)" msgstr "شنت (Shunt)"
@@ -1782,6 +1788,9 @@ msgstr "فقط گره‌های نوع Xray یا Sing-box پشتیبانی می
msgid "Chain Proxy" msgid "Chain Proxy"
msgstr "پروکسی زنجیره‌ای" msgstr "پروکسی زنجیره‌ای"
msgid "Outbound Interface"
msgstr "رابط خروجی"
msgid "Landing Node" msgid "Landing Node"
msgstr "گره فرود (Landing Node)" msgstr "گره فرود (Landing Node)"
+11 -2
View File
@@ -46,8 +46,8 @@ msgstr "Список правил"
msgid "Access control" msgid "Access control"
msgstr "Контроль доступа" msgstr "Контроль доступа"
msgid "Watch Logs" msgid "Runtime Logs"
msgstr "Просмотр журнала" msgstr "Журналы выполнения"
msgid "Node Config" msgid "Node Config"
msgstr "Параметры узла" msgstr "Параметры узла"
@@ -433,6 +433,12 @@ msgstr "Количество предпочтительных узлов"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them." msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
msgstr "Балансировщик выбирает указанное количество наилучших узлов и распределяет трафик между ними случайным образом." msgstr "Балансировщик выбирает указанное количество наилучших узлов и распределяет трафик между ними случайным образом."
msgid "Failure Tolerance (%)"
msgstr "Допуск на отказ (%)"
msgid "The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate."
msgstr "Максимально допустимый процент отказов при проверке скорости. Например, 1 означает допустимый процент отказов в 1%."
msgid "Shunt" msgid "Shunt"
msgstr "Shunt (разделение трафика)" msgstr "Shunt (разделение трафика)"
@@ -1783,6 +1789,9 @@ msgstr "Поддерживаются только узлы типа Xray или
msgid "Chain Proxy" msgid "Chain Proxy"
msgstr "Цепочка прокси" msgstr "Цепочка прокси"
msgid "Outbound Interface"
msgstr "Исходящий интерфейс"
msgid "Landing Node" msgid "Landing Node"
msgstr "Сервер выхода" msgstr "Сервер выхода"
+11 -2
View File
@@ -46,8 +46,8 @@ msgstr "规则列表"
msgid "Access control" msgid "Access control"
msgstr "访问控制" msgstr "访问控制"
msgid "Watch Logs" msgid "Runtime Logs"
msgstr "查看日志" msgstr "运行日志"
msgid "Node Config" msgid "Node Config"
msgstr "节点配置" msgstr "节点配置"
@@ -433,6 +433,12 @@ msgstr "优选节点数量"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them." msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
msgstr "负载均衡器选出最优节点的个数,流量将在这几个节点中随机分配。" msgstr "负载均衡器选出最优节点的个数,流量将在这几个节点中随机分配。"
msgid "Failure Tolerance (%)"
msgstr "测速失败率容忍度(%)"
msgid "The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate."
msgstr "最多可接受的测速失败率,例如 1 表示允许 1% 的测速失败率。"
msgid "Shunt" msgid "Shunt"
msgstr "分流" msgstr "分流"
@@ -1768,6 +1774,9 @@ msgstr "仅支持 Xray 或 Sing-box 类型节点。"
msgid "Chain Proxy" msgid "Chain Proxy"
msgstr "链式代理" msgstr "链式代理"
msgid "Outbound Interface"
msgstr "出站接口"
msgid "Landing Node" msgid "Landing Node"
msgstr "落地节点" msgstr "落地节点"
+11 -2
View File
@@ -46,8 +46,8 @@ msgstr "規則列表"
msgid "Access control" msgid "Access control"
msgstr "訪問控制" msgstr "訪問控制"
msgid "Watch Logs" msgid "Runtime Logs"
msgstr "查看日誌" msgstr "運行日誌"
msgid "Node Config" msgid "Node Config"
msgstr "節點配置" msgstr "節點配置"
@@ -433,6 +433,12 @@ msgstr "優選節點數量"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them." msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
msgstr "負載均衡器選出最優節點的個數,流量將在這几個節點中随機分配。" msgstr "負載均衡器選出最優節點的個數,流量將在這几個節點中随機分配。"
msgid "Failure Tolerance (%)"
msgstr "測速失敗率容忍度(%)"
msgid "The maximum acceptable speed test failure rate. For example, 1 means allowing a 1% failure rate."
msgstr "最多可接受的測速失敗率,例如 1 表示允許 1% 的測速失敗率。"
msgid "Shunt" msgid "Shunt"
msgstr "分流" msgstr "分流"
@@ -1774,6 +1780,9 @@ msgstr "僅支援 Xray 或 Sing-box 類型節點。"
msgid "Chain Proxy" msgid "Chain Proxy"
msgstr "鏈式代理" msgstr "鏈式代理"
msgid "Outbound Interface"
msgstr "出站接口"
msgid "Landing Node" msgid "Landing Node"
msgstr "落地節點" msgstr "落地節點"
@@ -12,7 +12,7 @@
if [ "$ACTION" = "ifupdate" ]; then if [ "$ACTION" = "ifupdate" ]; then
USE_TABLES=$(get_cache_var "USE_TABLES") USE_TABLES=$(get_cache_var "USE_TABLES")
[ -n "$USE_TABLES" ] && ${APP_PATH}/${USE_TABLES}.sh update_wan_sets >/dev/null 2>&1 & [ -n "$USE_TABLES" ] && { ${APP_PATH}/${USE_TABLES}.sh update_wan_sets >/dev/null 2>&1 & }
exit 0 exit 0
fi fi
@@ -84,7 +84,7 @@ run_xray() {
json_add_string "loglevel" "${loglevel}" json_add_string "loglevel" "${loglevel}"
[ -n "$flag" ] && { [ -n "$flag" ] && {
pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1{print $1}' | xargs kill -9 >/dev/null 2>&1 busybox pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1{print $1}' | xargs kill -9 >/dev/null 2>&1
json_add_string "flag" "${flag}" json_add_string "flag" "${flag}"
} }
[ -n "$socks_address" ] && [ -n "$socks_port" ] && { [ -n "$socks_address" ] && [ -n "$socks_port" ] && {
@@ -112,12 +112,12 @@ run_xray() {
local direct_ipset_conf=${GLOBAL_ACL_PATH}/dns_${flag}_direct.conf local direct_ipset_conf=${GLOBAL_ACL_PATH}/dns_${flag}_direct.conf
[ -n "$(echo ${flag} | grep '^acl')" ] && direct_ipset_conf=${TMP_ACL_PATH}/${sid}/dns_${flag}_direct.conf [ -n "$(echo ${flag} | grep '^acl')" ] && direct_ipset_conf=${TMP_ACL_PATH}/${sid}/dns_${flag}_direct.conf
if [ "${nftflag}" = "1" ]; then if [ "${nftflag}" = "1" ]; then
local direct_nftset4="passwall2_${node}_white" local direct_nftset4="psw2_${node}_white"
local direct_nftset6="passwall2_${node}_white6" local direct_nftset6="psw2_${node}_white6"
local direct_nftset="4#inet#passwall2#${direct_nftset4},6#inet#passwall2#${direct_nftset6}" local direct_nftset="4#inet#passwall2#${direct_nftset4},6#inet#passwall2#${direct_nftset6}"
else else
local direct_ipset4="passwall2_${node}_white" local direct_ipset4="psw2_${node}_white"
local direct_ipset6="passwall2_${node}_white6" local direct_ipset6="psw2_${node}_white6"
local direct_ipset="${direct_ipset4},${direct_ipset6}" local direct_ipset="${direct_ipset4},${direct_ipset6}"
fi fi
run_ipset_dns_server listen_port=${direct_dnsmasq_listen_port} server_dns=${AUTO_DNS} ipset="${direct_ipset}" nftset="${direct_nftset}" config_file=${direct_ipset_conf} run_ipset_dns_server listen_port=${direct_dnsmasq_listen_port} server_dns=${AUTO_DNS} ipset="${direct_ipset}" nftset="${direct_nftset}" config_file=${direct_ipset_conf}
@@ -225,7 +225,7 @@ run_singbox() {
json_add_string "loglevel" "${loglevel}" json_add_string "loglevel" "${loglevel}"
[ -n "$flag" ] && { [ -n "$flag" ] && {
pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1{print $1}' | xargs kill -9 >/dev/null 2>&1 busybox pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1{print $1}' | xargs kill -9 >/dev/null 2>&1
json_add_string "flag" "${flag}" json_add_string "flag" "${flag}"
} }
[ -n "$socks_address" ] && [ -n "$socks_port" ] && { [ -n "$socks_address" ] && [ -n "$socks_port" ] && {
@@ -251,12 +251,12 @@ run_singbox() {
local direct_ipset_conf=${GLOBAL_ACL_PATH}/dns_${flag}_direct.conf local direct_ipset_conf=${GLOBAL_ACL_PATH}/dns_${flag}_direct.conf
[ -n "$(echo ${flag} | grep '^acl')" ] && direct_ipset_conf=${TMP_ACL_PATH}/${sid}/dns_${flag}_direct.conf [ -n "$(echo ${flag} | grep '^acl')" ] && direct_ipset_conf=${TMP_ACL_PATH}/${sid}/dns_${flag}_direct.conf
if [ "${nftflag}" = "1" ]; then if [ "${nftflag}" = "1" ]; then
local direct_nftset4="passwall2_${node}_white" local direct_nftset4="psw2_${node}_white"
local direct_nftset6="passwall2_${node}_white6" local direct_nftset6="psw2_${node}_white6"
local direct_nftset="4#inet#passwall2#${direct_nftset4},6#inet#passwall2#${direct_nftset6}" local direct_nftset="4#inet#passwall2#${direct_nftset4},6#inet#passwall2#${direct_nftset6}"
else else
local direct_ipset4="passwall2_${node}_white" local direct_ipset4="psw2_${node}_white"
local direct_ipset6="passwall2_${node}_white6" local direct_ipset6="psw2_${node}_white6"
local direct_ipset="${direct_ipset4},${direct_ipset6}" local direct_ipset="${direct_ipset4},${direct_ipset6}"
fi fi
run_ipset_dns_server listen_port=${direct_dnsmasq_listen_port} server_dns=${AUTO_DNS} ipset="${direct_ipset}" nftset="${direct_nftset}" config_file=${direct_ipset_conf} run_ipset_dns_server listen_port=${direct_dnsmasq_listen_port} server_dns=${AUTO_DNS} ipset="${direct_ipset}" nftset="${direct_nftset}" config_file=${direct_ipset_conf}
@@ -386,6 +386,10 @@ run_socks() {
fi fi
fi fi
if [ -n "${error_msg}" ] && ([ -n "$(config_n_get $node hysteria_hop)" ] || [ -n "$(config_n_get $node hysteria2_hop)" ] || [ "$(config_n_get $node hysteria2_realms)" = "1" ]); then
unset error_msg
fi
[ -n "${error_msg}" ] && { [ -n "${error_msg}" ] && {
[ "$bind" != "127.0.0.1" ] && log 1 "$(i18n "Socks node: [%s]%s, start failed %s:%s %s" "${remarks}" "${tmp}" "${bind}" "${socks_port}" "${error_msg}")" [ "$bind" != "127.0.0.1" ] && log 1 "$(i18n "Socks node: [%s]%s, start failed %s:%s %s" "${remarks}" "${tmp}" "${bind}" "${socks_port}" "${error_msg}")"
return 1 return 1
@@ -555,7 +559,7 @@ socks_node_switch() {
[ -s "$pf" ] && kill -9 "$(head -n1 "$pf")" >/dev/null 2>&1 [ -s "$pf" ] && kill -9 "$(head -n1 "$pf")" >/dev/null 2>&1
done done
pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1 && !/acl\/|acl_/{print $1}' | xargs kill -9 >/dev/null 2>&1 busybox pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1 && !/acl\/|acl_/{print $1}' | xargs kill -9 >/dev/null 2>&1
for prefix in "" "HTTP_" "HTTP2"; do for prefix in "" "HTTP_" "HTTP2"; do
rm -rf "$TMP_PATH/${prefix}SOCKS_${flag}"* rm -rf "$TMP_PATH/${prefix}SOCKS_${flag}"*
done done
@@ -791,7 +795,7 @@ start_socks() {
# Auto switch logic # Auto switch logic
local enable_autoswitch=$(config_n_get $id enable_autoswitch 0) local enable_autoswitch=$(config_n_get $id enable_autoswitch 0)
[ "$enable_autoswitch" = "1" ] && $APP_PATH/socks_auto_switch.sh ${id} > /dev/null 2>&1 & [ "$enable_autoswitch" = "1" ] && { $APP_PATH/socks_auto_switch.sh ${id} > /dev/null 2>&1 & }
done done
} }
} }
@@ -805,14 +809,14 @@ clean_crontab() {
sed -i "/$(echo "lua ${APP_PATH}/rule_update.lua log" | sed 's#\/#\\\/#g')/d" /etc/crontabs/root >/dev/null 2>&1 sed -i "/$(echo "lua ${APP_PATH}/rule_update.lua log" | sed 's#\/#\\\/#g')/d" /etc/crontabs/root >/dev/null 2>&1
sed -i "/$(echo "lua ${APP_PATH}/subscribe.lua start" | sed 's#\/#\\\/#g')/d" /etc/crontabs/root >/dev/null 2>&1 sed -i "/$(echo "lua ${APP_PATH}/subscribe.lua start" | sed 's#\/#\\\/#g')/d" /etc/crontabs/root >/dev/null 2>&1
pgrep -af "${CONFIG}/" | awk '/tasks\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1 busybox pgrep -af "${CONFIG}/" | awk '/tasks\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
rm -rf /tmp/lock/${CONFIG}_tasks.lock rm -rf /tmp/lock/${CONFIG}_tasks.lock
} }
start_crontab() { start_crontab() {
if [ "$ENABLED_DEFAULT_ACL" == 1 ] || [ "$ENABLED_ACLS" == 1 ]; then if [ "$ENABLED_DEFAULT_ACL" == 1 ] || [ "$ENABLED_ACLS" == 1 ]; then
start_daemon=$(config_t_get global_delay start_daemon 0) start_daemon=$(config_t_get global_delay start_daemon 0)
[ "$start_daemon" = "1" ] && $APP_PATH/monitor.sh > /dev/null 2>&1 & [ "$start_daemon" = "1" ] && { $APP_PATH/monitor.sh > /dev/null 2>&1 & }
fi fi
[ -f "/tmp/lock/${CONFIG}_cron.lock" ] && { [ -f "/tmp/lock/${CONFIG}_cron.lock" ] && {
@@ -982,11 +986,11 @@ run_ipset_chinadns_ng() {
[ -n "${ipset}" ] && { [ -n "${ipset}" ] && {
set_names=$ipset set_names=$ipset
vps_set_names="passwall2_vps,passwall2_vps6" vps_set_names="psw2_vps,psw2_vps6"
} }
[ -n "${nftset}" ] && { [ -n "${nftset}" ] && {
set_names=$(echo ${nftset} | awk -F, '{printf "%s,%s", substr($1,3), substr($2,3)}' | sed 's/#/@/g') set_names=$(echo ${nftset} | awk -F, '{printf "%s,%s", substr($1,3), substr($2,3)}' | sed 's/#/@/g')
vps_set_names="inet@passwall2@passwall2_vps,inet@passwall2@passwall2_vps6" vps_set_names="inet@passwall2@psw2_vps,inet@passwall2@psw2_vps6"
} }
cat <<-EOF > $config_file cat <<-EOF > $config_file
bind-addr 127.0.0.1 bind-addr 127.0.0.1
@@ -1162,7 +1166,7 @@ acl_app() {
} }
start() { start() {
pgrep -f /tmp/etc/passwall2/bin > /dev/null 2>&1 && { busybox pgrep -f /tmp/etc/passwall2/bin > /dev/null 2>&1 && {
#log_i18n 0 "The program has started. Please stop it and then restart it!" #log_i18n 0 "The program has started. Please stop it and then restart it!"
stop stop
} }
@@ -1241,8 +1245,8 @@ stop() {
kill -9 "$pid" >/dev/null 2>&1 kill -9 "$pid" >/dev/null 2>&1
fi fi
done done
pgrep -f "sleep.*(6s|9s|58s)" | xargs kill -9 >/dev/null 2>&1 busybox pgrep -f "sleep.*(6s|9s|58s)" | xargs kill -9 >/dev/null 2>&1
pgrep -af "${CONFIG}/" | awk '! /app\.sh|subscribe\.lua|rule_update\.lua|tasks\.sh|server_app\.lua|ujail/{print $1}' | xargs kill -9 >/dev/null 2>&1 busybox pgrep -af "${CONFIG}/" | awk '! /app\.sh|subscribe\.lua|rule_update\.lua|tasks\.sh|server_app\.lua|ujail/{print $1}' | xargs kill -9 >/dev/null 2>&1
unset V2RAY_LOCATION_ASSET unset V2RAY_LOCATION_ASSET
unset XRAY_LOCATION_ASSET unset XRAY_LOCATION_ASSET
unset SS_SYSTEM_DNS_RESOLVER_FORCE_BUILTIN unset SS_SYSTEM_DNS_RESOLVER_FORCE_BUILTIN
@@ -8,7 +8,8 @@ listen_port=$2
server_address=$3 server_address=$3
server_port=$4 server_port=$4
pgrep -af "${CONFIG}/" | grep -E 'app\.sh.*(start|stop)|nftables\.sh|iptables\.sh|subscribe\.lua' >/dev/null && { busybox pgrep -af "${CONFIG}/" | grep -E 'app\.sh.*(start|stop)|nftables\.sh|iptables\.sh|subscribe\.lua' >/dev/null && {
# No detection during specific task execution
exit 0 exit 0
} }
@@ -274,7 +274,7 @@ function add_rule(var)
if address == "engage.cloudflareclient.com" then return end if address == "engage.cloudflareclient.com" then return end
if datatypes.hostname(address) then if datatypes.hostname(address) then
set_domain_dns(address, fwd_dns) set_domain_dns(address, fwd_dns)
set_domain_ipset(address, setflag_4 .. "passwall2_vps," .. setflag_6 .. "passwall2_vps6") set_domain_ipset(address, setflag_4 .. "psw2_vps," .. setflag_6 .. "psw2_vps6")
end end
end end
process_address(t.address) process_address(t.address)
@@ -3,17 +3,17 @@
DIR="$(cd "$(dirname "$0")" && pwd)" DIR="$(cd "$(dirname "$0")" && pwd)"
MY_PATH=$DIR/iptables.sh MY_PATH=$DIR/iptables.sh
UTILS_PATH=$DIR/utils.sh UTILS_PATH=$DIR/utils.sh
IPSET_LOCAL="passwall2_local" IPSET_LOCAL="psw2_local"
IPSET_PROXY_LAN="passwall2_proxy_lan" IPSET_PROXY_LAN="psw2_proxy_lan"
IPSET_LAN="passwall2_lan" IPSET_LAN="psw2_lan"
IPSET_VPS="passwall2_vps" IPSET_VPS="psw2_vps"
IPSET_WAN="passwall2_wan" IPSET_WAN="psw2_wan"
IPSET_LOCAL6="passwall2_local6" IPSET_LOCAL6="psw2_local6"
IPSET_PROXY_LAN6="passwall2_proxy_lan6" IPSET_PROXY_LAN6="psw2_proxy_lan6"
IPSET_LAN6="passwall2_lan6" IPSET_LAN6="psw2_lan6"
IPSET_VPS6="passwall2_vps6" IPSET_VPS6="psw2_vps6"
IPSET_WAN6="passwall2_wan6" IPSET_WAN6="psw2_wan6"
FWMARK="0x50535732" FWMARK="0x50535732"
@@ -194,8 +194,8 @@ gen_shunt_list() {
for shunt_id in $shunt_ids; do for shunt_id in $shunt_ids; do
local shunt_node=$(config_n_get ${node} "${shunt_id}") local shunt_node=$(config_n_get ${node} "${shunt_id}")
[ -n "$shunt_node" ] && { [ -n "$shunt_node" ] && {
local ipset_v4="passwall2_${node}_${shunt_id}" local ipset_v4="psw2_${node}_${shunt_id}"
local ipset_v6="passwall2_${node}_${shunt_id}6" local ipset_v6="psw2_${node}_${shunt_id}6"
ipset -! create $ipset_v4 nethash maxelem 1048576 ipset -! create $ipset_v4 nethash maxelem 1048576
ipset -! create $ipset_v6 nethash family inet6 maxelem 1048576 ipset -! create $ipset_v6 nethash family inet6 maxelem 1048576
local outbound="redirect" local outbound="redirect"
@@ -219,22 +219,14 @@ gen_shunt_list() {
} }
local direct_ipset4=$(get_cache_var "node_${node}_direct_ipset4") local direct_ipset4=$(get_cache_var "node_${node}_direct_ipset4")
[ -n "${direct_ipset4}" ] && { [ -n "${direct_ipset4}" ] && {
ipset -! create ${direct_ipset4} nethash maxelem 1048576 timeout 259200 ipset -! create ${direct_ipset4} iphash maxelem 1048576 timeout 259200
_SHUNT_LIST4="${_SHUNT_LIST4} ${direct_ipset4}:direct" _SHUNT_LIST4="${_SHUNT_LIST4} ${direct_ipset4}:direct"
} }
local direct_ipset6=$(get_cache_var "node_${node}_direct_ipset6") local direct_ipset6=$(get_cache_var "node_${node}_direct_ipset6")
[ -n "${direct_ipset6}" ] && { [ -n "${direct_ipset6}" ] && {
ipset -! create ${direct_ipset6} nethash family inet6 maxelem 1048576 timeout 259200 ipset -! create ${direct_ipset6} iphash family inet6 maxelem 1048576 timeout 259200
_SHUNT_LIST6="${_SHUNT_LIST6} ${direct_ipset6}:direct" _SHUNT_LIST6="${_SHUNT_LIST6} ${direct_ipset6}:direct"
} }
[ "${preloading}" = "1" ] && [ -n "$default_node" ] && {
local ipset_v4="passwall2_${node}_default"
local ipset_v6="passwall2_${node}_default6"
ipset -! create $ipset_v4 nethash maxelem 1048576
ipset -! create $ipset_v6 nethash family inet6 maxelem 1048576
_SHUNT_LIST4="${_SHUNT_LIST4} ${ipset_v4}:${default_outbound}"
_SHUNT_LIST6="${_SHUNT_LIST6} ${ipset_v6}:${default_outbound}"
}
} }
[ -n "${_SHUNT_LIST4}" ] && eval ${shunt_list4_var_name}=\"${_SHUNT_LIST4}\" [ -n "${_SHUNT_LIST4}" ] && eval ${shunt_list4_var_name}=\"${_SHUNT_LIST4}\"
[ -n "${_SHUNT_LIST6}" ] && eval ${shunt_list6_var_name}=\"${_SHUNT_LIST6}\" [ -n "${_SHUNT_LIST6}" ] && eval ${shunt_list6_var_name}=\"${_SHUNT_LIST6}\"
@@ -618,6 +610,8 @@ filter_direct_node_list() {
} }
update_wan_sets() { update_wan_sets() {
[ -z "$(command -v get_wan_ips)" ] && . "$UTILS_PATH"
local WAN_IP=$(get_wan_ips ip4) local WAN_IP=$(get_wan_ips ip4)
[ -n "$WAN_IP" ] && { [ -n "$WAN_IP" ] && {
ipset -F "$IPSET_WAN" ipset -F "$IPSET_WAN"
@@ -641,13 +635,13 @@ add_firewall_rule() {
ipset -! create $IPSET_LOCAL nethash maxelem 1048576 ipset -! create $IPSET_LOCAL nethash maxelem 1048576
ipset -! create $IPSET_PROXY_LAN nethash maxelem 1048576 ipset -! create $IPSET_PROXY_LAN nethash maxelem 1048576
ipset -! create $IPSET_LAN nethash maxelem 1048576 ipset -! create $IPSET_LAN nethash maxelem 1048576
ipset -! create $IPSET_VPS nethash maxelem 1048576 ipset -! create $IPSET_VPS iphash maxelem 1048576
ipset -! create $IPSET_WAN nethash maxelem 1048576 ipset -! create $IPSET_WAN nethash maxelem 1048576
ipset -! create $IPSET_LOCAL6 nethash family inet6 maxelem 1048576 ipset -! create $IPSET_LOCAL6 nethash family inet6 maxelem 1048576
ipset -! create $IPSET_PROXY_LAN6 nethash family inet6 maxelem 1048576 ipset -! create $IPSET_PROXY_LAN6 nethash family inet6 maxelem 1048576
ipset -! create $IPSET_LAN6 nethash family inet6 maxelem 1048576 ipset -! create $IPSET_LAN6 nethash family inet6 maxelem 1048576
ipset -! create $IPSET_VPS6 nethash family inet6 maxelem 1048576 ipset -! create $IPSET_VPS6 iphash family inet6 maxelem 1048576
ipset -! create $IPSET_WAN6 nethash family inet6 maxelem 1048576 ipset -! create $IPSET_WAN6 nethash family inet6 maxelem 1048576
get_local_ips ip4 | sed "s/^/add $IPSET_LOCAL /" | ipset -! -R get_local_ips ip4 | sed "s/^/add $IPSET_LOCAL /" | ipset -! -R
@@ -1025,7 +1019,7 @@ del_firewall_rule() {
flush_ipset() { flush_ipset() {
log_i18n 0 "Clear %s." "IPSet" log_i18n 0 "Clear %s." "IPSet"
for _name in $(ipset list | grep "Name: " | grep "passwall2_" | awk '{print $2}'); do for _name in $(ipset list | grep "Name: " | grep "psw2_" | awk '{print $2}'); do
destroy_ipset ${_name} destroy_ipset ${_name}
done done
} }
@@ -1105,7 +1099,7 @@ start() {
} }
stop() { stop() {
[ -z "$(command -v log_i18n)" ] && . /usr/share/passwall2/utils.sh [ -z "$(command -v log_i18n)" ] && . "$UTILS_PATH"
del_firewall_rule del_firewall_rule
destroy_ipset $IPSET_PROXY_LAN destroy_ipset $IPSET_PROXY_LAN
destroy_ipset $IPSET_PROXY_LAN6 destroy_ipset $IPSET_PROXY_LAN6
@@ -18,7 +18,7 @@ while [ "$ENABLED" -eq 1 ]; do
for filename in $(ls ${TMP_SCRIPT_FUNC_PATH} | grep -v "^_"); do for filename in $(ls ${TMP_SCRIPT_FUNC_PATH} | grep -v "^_"); do
cmd=$(cat ${TMP_SCRIPT_FUNC_PATH}/${filename}) cmd=$(cat ${TMP_SCRIPT_FUNC_PATH}/${filename})
cmd_check=$(echo $cmd | awk -F '>' '{print $1}') cmd_check=$(echo $cmd | awk -F '>' '{print $1}')
icount=$(pgrep -f "$(echo $cmd_check)" | wc -l) icount=$(busybox pgrep -f "$(echo $cmd_check)" | wc -l)
if [ $icount = 0 ]; then if [ $icount = 0 ]; then
#echo "${cmd} crashed, restarting." >> /tmp/log/passwall2.log #echo "${cmd} crashed, restarting." >> /tmp/log/passwall2.log
eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 & eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 &
@@ -4,17 +4,17 @@ DIR="$(cd "$(dirname "$0")" && pwd)"
MY_PATH=$DIR/nftables.sh MY_PATH=$DIR/nftables.sh
UTILS_PATH=$DIR/utils.sh UTILS_PATH=$DIR/utils.sh
NFTABLE_NAME="inet passwall2" NFTABLE_NAME="inet passwall2"
NFTSET_LOCAL="passwall2_local" NFTSET_LOCAL="psw2_local"
NFTSET_PROXY_LAN="passwall2_proxy_lan" NFTSET_PROXY_LAN="psw2_proxy_lan"
NFTSET_LAN="passwall2_lan" NFTSET_LAN="psw2_lan"
NFTSET_VPS="passwall2_vps" NFTSET_VPS="psw2_vps"
NFTSET_WAN="passwall2_wan" NFTSET_WAN="psw2_wan"
NFTSET_LOCAL6="passwall2_local6" NFTSET_LOCAL6="psw2_local6"
NFTSET_PROXY_LAN6="passwall2_proxy_lan6" NFTSET_PROXY_LAN6="psw2_proxy_lan6"
NFTSET_LAN6="passwall2_lan6" NFTSET_LAN6="psw2_lan6"
NFTSET_VPS6="passwall2_vps6" NFTSET_VPS6="psw2_vps6"
NFTSET_WAN6="passwall2_wan6" NFTSET_WAN6="psw2_wan6"
FWMARK="0x50535732" FWMARK="0x50535732"
@@ -231,8 +231,8 @@ gen_shunt_list() {
for shunt_id in $shunt_ids; do for shunt_id in $shunt_ids; do
local shunt_node=$(config_n_get ${node} "${shunt_id}") local shunt_node=$(config_n_get ${node} "${shunt_id}")
[ -n "$shunt_node" ] && { [ -n "$shunt_node" ] && {
local nftset_v4="passwall2_${node}_${shunt_id}" local nftset_v4="psw2_${node}_${shunt_id}"
local nftset_v6="passwall2_${node}_${shunt_id}6" local nftset_v6="psw2_${node}_${shunt_id}6"
gen_nftset $nftset_v4 ipv4_addr 0 0 gen_nftset $nftset_v4 ipv4_addr 0 0
gen_nftset $nftset_v6 ipv6_addr 0 0 gen_nftset $nftset_v6 ipv6_addr 0 0
local outbound="redirect" local outbound="redirect"
@@ -263,14 +263,6 @@ gen_shunt_list() {
gen_nftset $direct_nftset6 ipv6_addr 0 0 gen_nftset $direct_nftset6 ipv6_addr 0 0
_SHUNT_LIST6="${_SHUNT_LIST6} ${direct_nftset6}:direct" _SHUNT_LIST6="${_SHUNT_LIST6} ${direct_nftset6}:direct"
} }
[ "${preloading}" = "1" ] && [ -n "$default_node" ] && {
local nftset_v4="passwall2_${node}_default"
local nftset_v6="passwall2_${node}_default6"
gen_nftset $nftset_v4 ipv4_addr 0 0
gen_nftset $nftset_v6 ipv6_addr 0 0
_SHUNT_LIST4="${_SHUNT_LIST4} ${nftset_v4}:${default_outbound}"
_SHUNT_LIST6="${_SHUNT_LIST6} ${nftset_v6}:${default_outbound}"
}
} }
[ -n "${_SHUNT_LIST4}" ] && eval ${shunt_list4_var_name}=\"${_SHUNT_LIST4}\" [ -n "${_SHUNT_LIST4}" ] && eval ${shunt_list4_var_name}=\"${_SHUNT_LIST4}\"
[ -n "${_SHUNT_LIST6}" ] && eval ${shunt_list6_var_name}=\"${_SHUNT_LIST6}\" [ -n "${_SHUNT_LIST6}" ] && eval ${shunt_list6_var_name}=\"${_SHUNT_LIST6}\"
@@ -681,6 +673,8 @@ mwan3_start() {
} }
update_wan_sets() { update_wan_sets() {
[ -z "$(command -v get_wan_ips)" ] && . "$UTILS_PATH"
local WAN_IP=$(get_wan_ips ip4) local WAN_IP=$(get_wan_ips ip4)
[ -n "$WAN_IP" ] && { [ -n "$WAN_IP" ] && {
nft flush set $NFTABLE_NAME $NFTSET_WAN nft flush set $NFTABLE_NAME $NFTSET_WAN
@@ -1073,7 +1067,7 @@ del_firewall_rule() {
flush_nftset() { flush_nftset() {
log_i18n 0 "Clear %s." "NFTSet" log_i18n 0 "Clear %s." "NFTSet"
for _name in $(nft -a list sets | grep -E "passwall2_" | awk -F 'set ' '{print $2}' | awk '{print $1}'); do for _name in $(nft -a list sets | grep -E "psw2_" | awk -F 'set ' '{print $2}' | awk '{print $1}'); do
destroy_nftset ${_name} destroy_nftset ${_name}
done done
} }
@@ -1116,7 +1110,7 @@ start() {
} }
stop() { stop() {
[ -z "$(command -v log_i18n)" ] && . /usr/share/passwall2/utils.sh [ -z "$(command -v log_i18n)" ] && . "$UTILS_PATH"
del_firewall_rule del_firewall_rule
destroy_nftset $NFTSET_PROXY_LAN destroy_nftset $NFTSET_PROXY_LAN
destroy_nftset $NFTSET_PROXY_LAN6 destroy_nftset $NFTSET_PROXY_LAN6
@@ -6,7 +6,7 @@ APP_FILE=${APP_PATH}/app.sh
flag=0 flag=0
check_process() { check_process() {
while pgrep -af "${CONFIG}/" | grep -E 'app\.sh.*(start|stop)|nftables\.sh|iptables\.sh|subscribe\.lua' >/dev/null; do while busybox pgrep -af "${CONFIG}/" | grep -E 'app\.sh.*(start|stop)|nftables\.sh|iptables\.sh|subscribe\.lua' >/dev/null; do
sleep 6s sleep 6s
done done
} }
@@ -63,7 +63,7 @@ test_node() {
# Kill the SS plugin process # Kill the SS plugin process
local pid_file="/tmp/etc/${CONFIG}/test_node_${node_id}_plugin.pid" local pid_file="/tmp/etc/${CONFIG}/test_node_${node_id}_plugin.pid"
[ -s "$pid_file" ] && kill -9 "$(head -n 1 "$pid_file")" >/dev/null 2>&1 [ -s "$pid_file" ] && kill -9 "$(head -n 1 "$pid_file")" >/dev/null 2>&1
pgrep -af "test_node_${node_id}" | awk '! /socks_auto_switch\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1 busybox pgrep -af "test_node_${node_id}" | awk '! /socks_auto_switch\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
rm -rf /tmp/etc/${CONFIG}/test_node_${node_id}*.* rm -rf /tmp/etc/${CONFIG}/test_node_${node_id}*.*
if [ "${_proxy_status}" -eq 200 ]; then if [ "${_proxy_status}" -eq 200 ]; then
return 0 return 0
@@ -1688,22 +1688,19 @@ local function processData(szType, content, add_mode, group, sub_cfg)
local insecure = params.allowinsecure or params.insecure local insecure = params.allowinsecure or params.insecure
result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (sub_allowinsecure and "1" or "0") result.tls_allowInsecure = (insecure == "1" or insecure == "0") and insecure or (sub_allowinsecure and "1" or "0")
result.hysteria2_hop = params.mport result.hysteria2_hop = params.mport
if params["obfs-password"] or params["obfs_password"] then
result.hysteria2_obfs_type = "salamander"
result.hysteria2_obfs_password = params["obfs-password"] or params["obfs_password"]
end
if (sub_hysteria2_type == "sing-box" and has_singbox) or (sub_hysteria2_type == "xray" and has_xray) then if (sub_hysteria2_type == "sing-box" and has_singbox) or (sub_hysteria2_type == "xray" and has_xray) then
local is_singbox = sub_hysteria2_type == "sing-box" and has_singbox local is_singbox = sub_hysteria2_type == "sing-box" and has_singbox
result.type = is_singbox and 'sing-box' or 'Xray' result.type = is_singbox and 'sing-box' or 'Xray'
result.protocol = "hysteria2" result.protocol = "hysteria2"
if params["obfs-password"] or params["obfs_password"] then
result.hysteria2_obfs_type = "salamander"
result.hysteria2_obfs_password = params["obfs-password"] or params["obfs_password"]
end
result.use_finalmask = (params.fm and params.fm ~= "") and "1" or nil result.use_finalmask = (params.fm and params.fm ~= "") and "1" or nil
result.finalmask = (params.fm and params.fm ~= "") and api.base64Encode(params.fm) or nil result.finalmask = (params.fm and params.fm ~= "") and api.base64Encode(params.fm) or nil
elseif has_hysteria2 then elseif has_hysteria2 then
result.type = "Hysteria2" result.type = "Hysteria2"
if params["obfs-password"] or params["obfs_password"] then
result.hysteria2_obfs = params["obfs-password"] or params["obfs_password"]
end
else else
log(2, i18n.translatef("Skipping the %s node is due to incompatibility with the %s core program or incorrect node usage type settings.", "Hysteria2", "Hysteria2")) log(2, i18n.translatef("Skipping the %s node is due to incompatibility with the %s core program or incorrect node usage type settings.", "Hysteria2", "Hysteria2"))
return nil return nil
@@ -1761,7 +1758,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
end end
result.tls_serverName = params.sni result.tls_serverName = params.sni
result.tls_disable_sni = params.disable_sni result.tls_disable_sni = params.disable_sni
result.tuic_alpn = params.alpn or "default" result.tuic_alpn = params.alpn or "h3"
result.tuic_congestion_control = params.congestion_control or "cubic" result.tuic_congestion_control = params.congestion_control or "cubic"
result.tuic_udp_relay_mode = params.udp_relay_mode or "native" result.tuic_udp_relay_mode = params.udp_relay_mode or "native"
local insecure = params.allowinsecure or params.insecure or params.allow_insecure local insecure = params.allowinsecure or params.insecure or params.allow_insecure
@@ -2049,12 +2046,12 @@ local function select_node(nodes, config, parentConfig)
end end
end end
end end
-- First priority: Type + Notes + IP + Port -- First priority: Type + Notes + IP + Port + Group
if not server then if not server then
for index, node in pairs(nodes) do for index, node in pairs(nodes) do
if config.currentNode.type and config.currentNode.remarks and config.currentNode.address and config.currentNode.port then if config.currentNode.type and config.currentNode.remarks and config.currentNode.address and config.currentNode.port then
if node.type and node.remarks and node.address and node.port then if node.type and node.remarks and node.address and node.port then
if node.type == config.currentNode.type and node.remarks == config.currentNode.remarks and (node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port) then if node.type == config.currentNode.type and node.remarks == config.currentNode.remarks and (node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port) and node.group == config.currentNode.group then
if config.log == nil or config.log == true then if config.log == nil or config.log == true then
log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("First Matching node:") .. " " .. node.remarks) log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("First Matching node:") .. " " .. node.remarks)
end end
@@ -2065,12 +2062,12 @@ local function select_node(nodes, config, parentConfig)
end end
end end
end end
-- Second priority: Type + IP + Port -- Second priority: Type + IP + Port + Group
if not server then if not server then
for index, node in pairs(nodes) do for index, node in pairs(nodes) do
if config.currentNode.type and config.currentNode.address and config.currentNode.port then if config.currentNode.type and config.currentNode.address and config.currentNode.port then
if node.type and node.address and node.port then if node.type and node.address and node.port then
if node.type == config.currentNode.type and (node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port) then if node.type == config.currentNode.type and (node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port) and node.group == config.currentNode.group then
if config.log == nil or config.log == true then if config.log == nil or config.log == true then
log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Second Matching node:") .. " " .. node.remarks) log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Second Matching node:") .. " " .. node.remarks)
end end
@@ -2081,12 +2078,12 @@ local function select_node(nodes, config, parentConfig)
end end
end end
end end
-- Third priority: IP + Port -- Third priority: IP + Port + Group
if not server then if not server then
for index, node in pairs(nodes) do for index, node in pairs(nodes) do
if config.currentNode.address and config.currentNode.port then if config.currentNode.address and config.currentNode.port then
if node.address and node.port then if node.address and node.port then
if node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port then if node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port and node.group == config.currentNode.group then
if config.log == nil or config.log == true then if config.log == nil or config.log == true then
log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Third Matching node:") .. " " .. node.remarks) log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Third Matching node:") .. " " .. node.remarks)
end end
@@ -2097,12 +2094,12 @@ local function select_node(nodes, config, parentConfig)
end end
end end
end end
-- Fourth priority: IP -- Fourth priority: IP + Group
if not server then if not server then
for index, node in pairs(nodes) do for index, node in pairs(nodes) do
if config.currentNode.address then if config.currentNode.address then
if node.address then if node.address then
if node.address == config.currentNode.address then if node.address == config.currentNode.address and node.group == config.currentNode.group then
if config.log == nil or config.log == true then if config.log == nil or config.log == true then
log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Fourth Matching node:") .. " " .. node.remarks) log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Fourth Matching node:") .. " " .. node.remarks)
end end
@@ -2113,12 +2110,12 @@ local function select_node(nodes, config, parentConfig)
end end
end end
end end
-- Fifth priority: remarks -- Fifth priority: remarks + Group
if not server then if not server then
for index, node in pairs(nodes) do for index, node in pairs(nodes) do
if config.currentNode.remarks then if config.currentNode.remarks then
if node.remarks then if node.remarks then
if node.remarks == config.currentNode.remarks then if node.remarks == config.currentNode.remarks and node.group == config.currentNode.group then
if config.log == nil or config.log == true then if config.log == nil or config.log == true then
log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Fifth Matching node:") .. " " .. node.remarks) log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Fifth Matching node:") .. " " .. node.remarks)
end end
@@ -22,7 +22,7 @@ do
stop_interval_mode=$(expr "$stop_interval_mode" \* 60) stop_interval_mode=$(expr "$stop_interval_mode" \* 60)
if [ -n "$stop_week_mode" ]; then if [ -n "$stop_week_mode" ]; then
[ "$stop_week_mode" = "8" ] && { [ "$stop_week_mode" = "8" ] && {
[ "$(expr "$CFG_UPDATE_INT" % "$stop_interval_mode")" -eq 0 ] && /etc/init.d/$CONFIG stop > /dev/null 2>&1 & [ "$(expr "$CFG_UPDATE_INT" % "$stop_interval_mode")" -eq 0 ] && { /etc/init.d/$CONFIG stop > /dev/null 2>&1 & }
} }
fi fi
@@ -31,7 +31,7 @@ do
start_interval_mode=$(expr "$start_interval_mode" \* 60) start_interval_mode=$(expr "$start_interval_mode" \* 60)
if [ -n "$start_week_mode" ]; then if [ -n "$start_week_mode" ]; then
[ "$start_week_mode" = "8" ] && { [ "$start_week_mode" = "8" ] && {
[ "$(expr "$CFG_UPDATE_INT" % "$start_interval_mode")" -eq 0 ] && /etc/init.d/$CONFIG start > /dev/null 2>&1 & [ "$(expr "$CFG_UPDATE_INT" % "$start_interval_mode")" -eq 0 ] && { /etc/init.d/$CONFIG start > /dev/null 2>&1 & }
} }
fi fi
@@ -40,7 +40,7 @@ do
restart_interval_mode=$(expr "$restart_interval_mode" \* 60) restart_interval_mode=$(expr "$restart_interval_mode" \* 60)
if [ -n "$restart_week_mode" ]; then if [ -n "$restart_week_mode" ]; then
[ "$restart_week_mode" = "8" ] && { [ "$restart_week_mode" = "8" ] && {
[ "$(expr "$CFG_UPDATE_INT" % "$restart_interval_mode")" -eq 0 ] && /etc/init.d/$CONFIG restart > /dev/null 2>&1 & [ "$(expr "$CFG_UPDATE_INT" % "$restart_interval_mode")" -eq 0 ] && { /etc/init.d/$CONFIG restart > /dev/null 2>&1 & }
} }
fi fi
@@ -50,7 +50,7 @@ do
hourupdate=$(expr "$hourupdate" \* 60) hourupdate=$(expr "$hourupdate" \* 60)
if [ "$autoupdate" = "1" ]; then if [ "$autoupdate" = "1" ]; then
[ "$weekupdate" = "8" ] && { [ "$weekupdate" = "8" ] && {
[ "$(expr "$CFG_UPDATE_INT" % "$hourupdate")" -eq 0 ] && lua $APP_PATH/rule_update.lua log all cron > /dev/null 2>&1 & [ "$(expr "$CFG_UPDATE_INT" % "$hourupdate")" -eq 0 ] && { lua $APP_PATH/rule_update.lua log all cron > /dev/null 2>&1 & }
} }
fi fi
@@ -73,7 +73,7 @@ do
hour_update=$(expr "$hour_update" \* 60) hour_update=$(expr "$hour_update" \* 60)
cfgids=$(echo -n $(cat ${TMP_SUB_PATH}/${name}) | sed 's# #,#g') cfgids=$(echo -n $(cat ${TMP_SUB_PATH}/${name}) | sed 's# #,#g')
[ "$week_update" = "8" ] && { [ "$week_update" = "8" ] && {
[ "$(expr "$CFG_UPDATE_INT" % "$hour_update")" -eq 0 ] && lua $APP_PATH/subscribe.lua start $cfgids cron > /dev/null 2>&1 & [ "$(expr "$CFG_UPDATE_INT" % "$hour_update")" -eq 0 ] && { lua $APP_PATH/subscribe.lua start $cfgids cron > /dev/null 2>&1 & }
} }
done done
@@ -50,10 +50,13 @@ url_test_node() {
NO_REC_PROCESS=1 /usr/share/${CONFIG}/app.sh run_socks flag="url_test_${node_id}" node=${node_id} bind=127.0.0.1 socks_port=${_tmp_port} config_file=url_test_${node_id}.json NO_REC_PROCESS=1 /usr/share/${CONFIG}/app.sh run_socks flag="url_test_${node_id}" node=${node_id} bind=127.0.0.1 socks_port=${_tmp_port} config_file=url_test_${node_id}.json
sleep 2s sleep 2s
local curlx="socks5h://127.0.0.1:${_tmp_port}" local curlx="socks5h://127.0.0.1:${_tmp_port}"
local url=$(config_t_get global_other url_test_url https://www.google.com/generate_204) local probeUrl=$(config_t_get global_other url_test_url https://www.google.com/generate_204)
result=$(curl --connect-timeout 3 -o /dev/null -I -skL -w "%{http_code}:%{time_starttransfer}" -x $curlx "${url}") result=$(curl --connect-timeout 3 --max-time 5 -o /dev/null -I -skL -w "%{http_code}:%{time_pretransfer}" -x ${curlx} "${probeUrl}")
pgrep -af "url_test_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1 # End the SS plugin process
rm -rf /tmp/etc/${CONFIG}/*url_test_${node_id}*.json local pid_file="/tmp/etc/${CONFIG}/url_test_${node_id}_plugin.pid"
[ -s "$pid_file" ] && kill -9 "$(head -n 1 "$pid_file")" >/dev/null 2>&1
busybox pgrep -af "url_test_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
rm -rf /tmp/etc/${CONFIG}/*url_test_${node_id}*.*
} }
echo $result echo $result
} }
@@ -412,7 +412,7 @@ run_process_queue() {
for filename in $(ls ${TMP_PROCESS_LIST_PATH}); do for filename in $(ls ${TMP_PROCESS_LIST_PATH}); do
cmd=$(cat ${TMP_PROCESS_LIST_PATH}/${filename}) cmd=$(cat ${TMP_PROCESS_LIST_PATH}/${filename})
cmd_check=$(echo $cmd | awk -F '>' '{print $1}') cmd_check=$(echo $cmd | awk -F '>' '{print $1}')
icount=$(pgrep -f "$(echo $cmd_check)" | wc -l) icount=$(busybox pgrep -f "$(echo $cmd_check)" | wc -l)
if [ $icount = 0 ]; then if [ $icount = 0 ]; then
eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 & eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 &
fi fi
+1 -1
View File
@@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-xray PKG_NAME:=luci-app-xray
PKG_VERSION:=3.7.1 PKG_VERSION:=3.7.1
PKG_RELEASE:=1 PKG_RELEASE:=2
PKG_LICENSE:=MPLv2 PKG_LICENSE:=MPLv2
PKG_LICENSE_FILES:=LICENSE PKG_LICENSE_FILES:=LICENSE
@@ -261,7 +261,7 @@ function gen_config() {
const general = filter(values(config), k => k[".type"] == "general")[0] || {}; const general = filter(values(config), k => k[".type"] == "general")[0] || {};
const custom_configuration_hook = loadstring(general["custom_configuration_hook"] || "return i => i;")(); const custom_configuration_hook = loadstring(general["custom_configuration_hook"] || "return i => i;")();
return custom_configuration_hook({ let result = {
inbounds: inbounds(general, config, extra_inbound), inbounds: inbounds(general, config, extra_inbound),
outbounds: outbounds(general, config, manual_tproxy, bridge, extra_inbound, fakedns), outbounds: outbounds(general, config, manual_tproxy, bridge, extra_inbound, fakedns),
dns: dns_conf(general, config, manual_tproxy, fakedns), dns: dns_conf(general, config, manual_tproxy, fakedns),
@@ -274,15 +274,19 @@ function gen_config() {
place: "holder" place: "holder"
} : null, } : null,
observatory: observatory(general, manual_tproxy), observatory: observatory(general, manual_tproxy),
reverse: {
bridges: bridges(bridge)
},
routing: { routing: {
domainStrategy: general["routing_domain_strategy"] || "AsIs", domainStrategy: general["routing_domain_strategy"] || "AsIs",
rules: rules(general, bridge, manual_tproxy, extra_inbound, fakedns), rules: rules(general, bridge, manual_tproxy, extra_inbound, fakedns),
balancers: balancers(general, extra_inbound, fakedns) balancers: balancers(general, extra_inbound, fakedns)
} }
}); };
const bridges_deprecated = bridges(bridge);
if (length(bridges_deprecated) > 0) {
result["reverse"] = {
bridges: bridges_deprecated
};
};
return custom_configuration_hook(result);
} }
printf("%.4J", gen_config()); printf("%.4J", gen_config());
@@ -1,17 +1,176 @@
'use strict';'require baseclass';'require ui';return baseclass.extend({__init__:function(){ui.menu.load().then(L.bind(this.render,this));},render:function(tree){var node=tree,url='',children=ui.menu.getChildren(tree);for(var i=0;i<children.length;i++){var isActive=(L.env.requestpath.length?children[i].name==L.env.requestpath[0]:i==0);if(isActive) 'use strict';
this.renderMainMenu(children[i],children[i].name);} 'require baseclass';
if(L.env.dispatchpath.length>=3){for(var i=0;i<3&&node;i++){node=node.children[L.env.dispatchpath[i]];url=url+(url?'/':'')+L.env.dispatchpath[i];} 'require ui';
if(node)
this.renderTabMenu(node,url);} return baseclass.extend({
document.querySelector('a.showSide').addEventListener('click',ui.createHandlerFn(this,'handleSidebarToggle'));document.querySelector('.darkMask').addEventListener('click',ui.createHandlerFn(this,'handleSidebarToggle'));},handleMenuExpand:function(ev){var a=ev.target,slide=a.parentNode,slide_menu=a.nextElementSibling;var collapse=false;document.querySelectorAll('.main .main-left .nav > li >ul.active').forEach(function(ul){$(ul).stop(true).slideUp("fast",function(){ul.classList.remove('active');ul.previousElementSibling.classList.remove('active');});if(!collapse&&ul===slide_menu){collapse=true;}});if(!slide_menu) __init__: function () {
return;if(!collapse){$(slide).find(".slide-menu").slideDown("fast",function(){slide_menu.classList.add('active');a.classList.add('active');});a.blur();} ui.menu.load().then(L.bind(this.render, this));
ev.preventDefault();ev.stopPropagation();},renderMainMenu:function(tree,url,level){var l=(level||0)+1,ul=E('ul',{'class':level?'slide-menu':'nav'}),children=ui.menu.getChildren(tree);if(children.length==0||l>2) },
return E([]);for(var i=0;i<children.length;i++){var isActive=((L.env.dispatchpath[l]==children[i].name)&&(L.env.dispatchpath[l-1]==tree.name)),submenu=this.renderMainMenu(children[i],url+'/'+children[i].name,l),hasChildren=submenu.children.length,slideClass=hasChildren?'slide':null,menuClass=hasChildren?'menu':'food';if(isActive){ul.classList.add('active');slideClass+=" active";menuClass+=" active";}
ul.appendChild(E('li',{'class':slideClass},[E('a',{'href':L.url(url,children[i].name),'click':(l==1)?ui.createHandlerFn(this,'handleMenuExpand'):null,'class':menuClass,'data-title':hasChildren?children[i].title.replace(" ","_"):children[i].title.replace(" ","_"),},[_(children[i].title)]),submenu]));} render: function (tree) {
if(l==1){document.querySelector('#mainmenu').appendChild(ul);document.querySelector('#mainmenu').style.display='';} var node = tree,
return ul;},renderTabMenu:function(tree,url,level){var container=document.querySelector('#tabmenu'),l=(level||0)+1,ul=E('ul',{'class':'tabs'}),children=ui.menu.getChildren(tree),activeNode=null;if(children.length==0) url = '',
return E([]);for(var i=0;i<children.length;i++){var isActive=(L.env.dispatchpath[l+2]==children[i].name),activeClass=isActive?' active':'',className='tabmenu-item-%s %s'.format(children[i].name,activeClass);ul.appendChild(E('li',{'class':className},[E('a',{'href':L.url(url,children[i].name)},[_(children[i].title)])]));if(isActive) children = ui.menu.getChildren(tree);
activeNode=children[i];}
container.appendChild(ul);container.style.display='';if(activeNode) for (var i = 0; i < children.length; i++) {
container.appendChild(this.renderTabMenu(activeNode,url+'/'+activeNode.name,l));return ul;},handleSidebarToggle:function(ev){var showside=document.querySelector('a.showSide'),sidebar=document.querySelector('#mainmenu'),darkmask=document.querySelector('.darkMask'),scrollbar=document.querySelector('.main-right');if(showside.classList.contains('active')){showside.classList.remove('active');sidebar.classList.remove('active');scrollbar.classList.remove('active');darkmask.classList.remove('active');} var isActive =
else{showside.classList.add('active');sidebar.classList.add('active');scrollbar.classList.add('active');darkmask.classList.add('active');}}}); L.env.requestpath.length
? children[i].name == L.env.requestpath[0]
: i == 0;
if (isActive) this.renderMainMenu(children[i], children[i].name);
}
if (L.env.dispatchpath.length >= 3) {
for (var i = 0; i < 3 && node; i++) {
node = node.children[L.env.dispatchpath[i]];
url = url + (url ? '/' : '') + L.env.dispatchpath[i];
}
if (node) this.renderTabMenu(node, url);
}
document
.querySelector('a.showSide')
.addEventListener('click', ui.createHandlerFn(this, 'handleSidebarToggle'));
document
.querySelector('.darkMask')
.addEventListener('click', ui.createHandlerFn(this, 'handleSidebarToggle'));
},
handleMenuExpand: function (ev) {
var a = ev.target,
slide = a.parentNode,
slide_menu = a.nextElementSibling;
var collapse = false;
document
.querySelectorAll('.main .main-left .nav > li >ul.active')
.forEach(function (ul) {
$(ul).stop(true).slideUp('fast', function () {
ul.classList.remove('active');
ul.previousElementSibling.classList.remove('active');
});
if (!collapse && ul === slide_menu) {
collapse = true;
}
});
if (!slide_menu) return;
if (!collapse) {
$(slide)
.find('.slide-menu')
.slideDown('fast', function () {
slide_menu.classList.add('active');
a.classList.add('active');
});
a.blur();
}
ev.preventDefault();
ev.stopPropagation();
},
renderMainMenu: function (tree, url, level) {
var l = (level || 0) + 1,
ul = E('ul', { class: level ? 'slide-menu' : 'nav' }),
children = ui.menu.getChildren(tree);
if (children.length == 0 || l > 2) return E([]);
for (var i = 0; i < children.length; i++) {
var isActive =
L.env.dispatchpath[l] == children[i].name &&
L.env.dispatchpath[l - 1] == tree.name,
submenu = this.renderMainMenu(children[i], url + '/' + children[i].name, l),
hasChildren = submenu.children.length,
slideClass = hasChildren ? 'slide' : null,
menuClass = hasChildren ? 'menu' : 'food';
if (isActive) {
ul.classList.add('active');
slideClass += ' active';
menuClass += ' active';
}
ul.appendChild(
E(
'li',
{ class: slideClass },
[
E(
'a',
{
href: L.url(url, children[i].name),
click: l == 1 ? ui.createHandlerFn(this, 'handleMenuExpand') : null,
class: menuClass,
'data-title': hasChildren
? children[i].title.replace(' ', '_')
: children[i].title.replace(' ', '_'),
},
[_(children[i].title)]
),
submenu,
]
)
);
}
if (l == 1) {
document.querySelector('#mainmenu').appendChild(ul);
document.querySelector('#mainmenu').style.display = '';
}
return ul;
},
renderTabMenu: function (tree, url, level) {
var container = document.querySelector('#tabmenu'),
l = (level || 0) + 1,
ul = E('ul', { class: 'tabs' }),
children = ui.menu.getChildren(tree),
activeNode = null;
if (children.length == 0) return E([]);
for (var i = 0; i < children.length; i++) {
var isActive = L.env.dispatchpath[l + 2] == children[i].name,
activeClass = isActive ? ' active' : '',
className = 'tabmenu-item-%s %s'.format(children[i].name, activeClass);
ul.appendChild(
E('li', { class: className }, [
E('a', { href: L.url(url, children[i].name) }, [_(children[i].title)]),
])
);
if (isActive) activeNode = children[i];
}
container.appendChild(ul);
container.style.display = '';
if (activeNode)
container.appendChild(this.renderTabMenu(activeNode, url + '/' + activeNode.name, l));
return ul;
},
handleSidebarToggle: function (ev) {
var showside = document.querySelector('a.showSide'),
sidebar = document.querySelector('#mainmenu'),
darkmask = document.querySelector('.darkMask'),
scrollbar = document.querySelector('.main-right');
if (showside.classList.contains('active')) {
showside.classList.remove('active');
sidebar.classList.remove('active');
scrollbar.classList.remove('active');
darkmask.classList.remove('active');
} else {
showside.classList.add('active');
sidebar.classList.add('active');
scrollbar.classList.add('active');
darkmask.classList.add('active');
}
},
});
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -1,2 +1 @@
(function(undefined) {}).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
(function(undefined){}).call('object'===typeof window&&window||'object'===typeof self&&self||'object'===typeof global&&global||{});
+4 -4
View File
@@ -1,14 +1,14 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=mihomo-alpha PKG_NAME:=mihomo-alpha
PKG_VERSION:=2026.05.08 PKG_VERSION:=2026.05.16
PKG_RELEASE:=7 PKG_RELEASE:=8
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_PROTO:=git PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/MetaCubeX/mihomo.git PKG_SOURCE_URL:=https://github.com/MetaCubeX/mihomo.git
PKG_SOURCE_VERSION:=35d5d4e44d7a7b8352617b1fec7faf0ba26efc8a PKG_SOURCE_VERSION:=e9e23c4e98c34b158953f1414b317f25f3e3aa1e
PKG_MIRROR_HASH:=skip PKG_MIRROR_HASH:=skip
PKG_LICENSE:=GPL3.0+ PKG_LICENSE:=GPL3.0+
@@ -18,7 +18,7 @@ PKG_BUILD_DEPENDS:=golang/host
PKG_BUILD_PARALLEL:=1 PKG_BUILD_PARALLEL:=1
PKG_BUILD_FLAGS:=no-mips16 PKG_BUILD_FLAGS:=no-mips16
PKG_BUILD_VERSION:=alpha-35d5d4e4 PKG_BUILD_VERSION:=alpha-e9e23c4e
PKG_BUILD_TIME:=$(shell date -u -Iseconds) PKG_BUILD_TIME:=$(shell date -u -Iseconds)
GO_PKG:=github.com/metacubex/mihomo GO_PKG:=github.com/metacubex/mihomo
+4 -4
View File
@@ -1,14 +1,14 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=mihomo-meta PKG_NAME:=mihomo-meta
PKG_VERSION:=1.19.24 PKG_VERSION:=1.19.25
PKG_RELEASE:=4 PKG_RELEASE:=5
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_PROTO:=git PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/MetaCubeX/mihomo.git PKG_SOURCE_URL:=https://github.com/MetaCubeX/mihomo.git
PKG_SOURCE_VERSION:=v1.19.24 PKG_SOURCE_VERSION:=v1.19.25
PKG_MIRROR_HASH:=skip PKG_MIRROR_HASH:=skip
PKG_LICENSE:=GPL3.0+ PKG_LICENSE:=GPL3.0+
@@ -18,7 +18,7 @@ PKG_BUILD_DEPENDS:=golang/host
PKG_BUILD_PARALLEL:=1 PKG_BUILD_PARALLEL:=1
PKG_BUILD_FLAGS:=no-mips16 PKG_BUILD_FLAGS:=no-mips16
PKG_BUILD_VERSION:=v1.19.24 PKG_BUILD_VERSION:=v1.19.25
PKG_BUILD_TIME:=$(shell date -u -Iseconds) PKG_BUILD_TIME:=$(shell date -u -Iseconds)
GO_PKG:=github.com/metacubex/mihomo GO_PKG:=github.com/metacubex/mihomo
+2 -2
View File
@@ -5,8 +5,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=mihomo PKG_NAME:=mihomo
PKG_VERSION:=1.19.24 PKG_VERSION:=1.19.25
PKG_RELEASE:=8 PKG_RELEASE:=9
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/metacubex/mihomo/tar.gz/v$(PKG_VERSION)? PKG_SOURCE_URL:=https://codeload.github.com/metacubex/mihomo/tar.gz/v$(PKG_VERSION)?
+2 -2
View File
@@ -9,7 +9,7 @@ PKG_NAME:=natmapt
PKG_UPSTREAM_VERSION:=20260214 PKG_UPSTREAM_VERSION:=20260214
PKG_UPSTREAM_GITHASH:= PKG_UPSTREAM_GITHASH:=
PKG_VERSION:=$(PKG_UPSTREAM_VERSION)$(if $(PKG_UPSTREAM_GITHASH),~$(call version_abbrev,$(PKG_UPSTREAM_GITHASH))) PKG_VERSION:=$(PKG_UPSTREAM_VERSION)$(if $(PKG_UPSTREAM_GITHASH),~$(call version_abbrev,$(PKG_UPSTREAM_GITHASH)))
PKG_RELEASE:=3 PKG_RELEASE:=4
SCRIPTS_VERSION:=0.2026.01.24 SCRIPTS_VERSION:=0.2026.01.24
PKG_SOURCE_SUBDIR:=$(PKG_UPSTREAM_NAME)-$(PKG_UPSTREAM_VERSION) PKG_SOURCE_SUBDIR:=$(PKG_UPSTREAM_NAME)-$(PKG_UPSTREAM_VERSION)
@@ -23,7 +23,7 @@ PKG_SOURCE:=$(PKG_SOURCE_SUBDIR).tar.xz
else else
PKG_SOURCE_PROTO:=git PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/heiher/natmap.git PKG_SOURCE_URL:=https://github.com/heiher/natmap.git
PKG_SOURCE_VERSION:=079bce1d6c5798fa6f47d364b737762dae89aafc PKG_SOURCE_VERSION:=e0975d64fbd22a34e30d573e51c44fc017b3ff8c
PKG_MIRROR_HASH:=skip PKG_MIRROR_HASH:=skip
PKG_SOURCE:=$(PKG_SOURCE_SUBDIR)-$(PKG_SOURCE_VERSION).tar.gz PKG_SOURCE:=$(PKG_SOURCE_SUBDIR)-$(PKG_SOURCE_VERSION).tar.gz
+2 -2
View File
@@ -5,12 +5,12 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=rtl8189es PKG_NAME:=rtl8189es
PKG_RELEASE:=1 PKG_RELEASE:=2
PKG_SOURCE_URL:=https://github.com/jwrdegoede/rtl8189ES_linux.git PKG_SOURCE_URL:=https://github.com/jwrdegoede/rtl8189ES_linux.git
PKG_SOURCE_PROTO:=git PKG_SOURCE_PROTO:=git
PKG_SOURCE_DATE:=2025-09-27 PKG_SOURCE_DATE:=2025-09-27
PKG_SOURCE_VERSION:=0a5d04114fac3c9f48a343cb905fbb6a3f9f5df5 PKG_SOURCE_VERSION:=36d917d5069f25f4c36b8b4d8b17b5d02e9116e9
PKG_MIRROR_HASH:=skip PKG_MIRROR_HASH:=skip
PKG_BUILD_PARALLEL:=1 PKG_BUILD_PARALLEL:=1
+2 -2
View File
@@ -5,8 +5,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=tuic-client PKG_NAME:=tuic-client
PKG_VERSION:=1.8.1 PKG_VERSION:=1.8.2
PKG_RELEASE:=4 PKG_RELEASE:=5
PKG_LICENSE_FILES:=LICENSE PKG_LICENSE_FILES:=LICENSE
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org> PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>