🌈 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
PKG_NAME:=luci-app-amlogic
PKG_VERSION:=3.1.313
PKG_RELEASE:=11
PKG_VERSION:=3.1.315
PKG_RELEASE:=12
PKG_LICENSE:=GPL-2.0 License
PKG_MAINTAINER:=ophub <https://github.com/ophub/luci-app-amlogic>
@@ -162,6 +162,7 @@ const inbound_type = [
['anytls', _('AnyTLS') + ' - ' + _('TCP')],
['tuic', _('TUIC') + ' - ' + _('UDP')],
['hysteria2', _('Hysteria2') + ' - ' + _('UDP')],
['hysteria2-realm', _('Hysteria2 Realm') + ' - ' + _('TCP/UDP')],
['trusttunnel', _('TrustTunnel') + ' - ' + _('TCP/UDP')],
['tunnel', _('Tunnel') + ' - ' + _('TCP/UDP')]
];
@@ -118,6 +118,7 @@ function renderListeners(s, uciconfig, isClient) {
s.tab('field_general', _('General 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_transport', _('Transport fields'));
s.tab('field_multiplex', _('Multiplex fields'));
@@ -209,6 +210,36 @@ function renderListeners(s, uciconfig, isClient) {
o.depends('type', 'hysteria2');
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 */
o = s.taboption('field_general', form.ListValue, 'shadowsocks_chipher', _('Chipher'));
o.default = hm.shadowsocks_cipher_methods[1][0];
@@ -498,11 +529,18 @@ function renderListeners(s, uciconfig, isClient) {
/* Plugin fields */
o = s.taboption('field_general', form.ListValue, 'plugin', _('Plugin'));
o.value('', _('none'));
o.value('obfs', _('obfs-simple'));
o.value('shadow-tls', _('shadow-tls'));
//o.value('kcp-tun', _('kcp-tun'));
o.depends('type', 'shadowsocks');
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.datatype = 'hostport';
o.placeholder = 'cloud.tencent.com:443';
@@ -782,6 +820,40 @@ function renderListeners(s, uciconfig, isClient) {
o.depends('vless_decryption', '1');
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 */
o = s.taboption('field_general', form.Flag, 'tls', _('TLS'));
o.default = o.disabled;
@@ -795,12 +867,29 @@ function renderListeners(s, uciconfig, isClient) {
if (['trojan', 'anytls', 'tuic', 'hysteria2', 'trusttunnel'].includes(type)) {
tls.checked = true;
tls.disabled = true;
if (['tuic', 'hysteria2'].includes(type) && !`${tls_alpn.getValue()}`)
tls_alpn.setValue('h3');
} else {
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
if (['trusttunnel'].includes(type)) {
tls_alpn.node.querySelector('input').disabled = true;
@@ -817,7 +906,7 @@ function renderListeners(s, uciconfig, isClient) {
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 = 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) => {
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 = 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(/* ... */) {
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 = s.taboption('field_tls', form.Button, '_upload_client_auth_cert', _('Upload certificate') + _(' (mTLS)'),
@@ -918,13 +1007,13 @@ function renderListeners(s, uciconfig, isClient) {
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 = 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.'));
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;
// uTLS fields
@@ -221,6 +221,7 @@ return view.extend({
ss.tab('field_general', _('General 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_transport', _('Transport fields'));
ss.tab('field_multiplex', _('Multiplex fields'));
@@ -956,6 +957,40 @@ return view.extend({
so.depends('vless_encryption', '1');
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 */
so = ss.taboption('field_general', form.Flag, 'tls', _('TLS'));
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,
obfs: cfg.hysteria_obfs_type,
"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 */
"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-reusable-secs": cfg.transport_xhttp_xmux_max_reusable_secs,
"h-keep-alive-period": strToInt(cfg.transport_xhttp_xmux_keep_alive_period)
// @download-settings
} : null
} : null
} : {}),
@@ -251,6 +251,27 @@ export function parseListener(cfg, isClient, label) {
obfs: cfg.hysteria_obfs_type,
"obfs-password": cfg.hysteria_obfs_password,
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 */
cipher: cfg.shadowsocks_chipher,
@@ -302,6 +323,11 @@ export function parseListener(cfg, isClient, label) {
/* Plugin fields */
...(cfg.plugin ? {
// obfs-simple
"simple-obfs": cfg.plugin === 'obfs' ? {
enable: true,
mode: cfg.plugin_opts_obfsmode
} : null,
// shadow-tls
"shadow-tls": cfg.plugin === 'shadow-tls' ? {
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-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,
// @其他的配置
} : null
} : {})
}
+2 -2
View File
@@ -3,8 +3,8 @@ include $(TOPDIR)/rules.mk
LUCI_TITLE:=Information dashboard for 3G/LTE dongle
LUCI_DEPENDS:=+modeminfo
PKG_LICENSE:=GPLv3
PKG_VERSION:=0.4.6
PKG_RELEASE:=2
PKG_VERSION:=0.4.7
PKG_RELEASE:=3
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 mmdesc = _('Get device hardware name via mmcli utility if aviable.');
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');
return view.extend({
@@ -88,8 +88,12 @@ return view.extend({
s = m.section(form.TypedSection, 'general', _('General option'), null);
s.anonymous = true;
o = s.option(form.Flag, 'index', _('Index page'), idesc);
s.anonymous = true;
o = s.option(form.ListValue, 'index', _('Short info'), idesc);
o.widget="radio";
o.value(0,_('none'));
o.value(1,_('overview'));
o.value(2,_('menubar'));
o.default = '0';
o.rmempty = true;
o = s.option(form.Flag, 'decimail', _('Show decimal'), lacdec);
@@ -19,7 +19,11 @@ return baseclass.extend({
title: _('Cellular network'),
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) => {
const timeout = setTimeout(() => {
@@ -43,10 +47,15 @@ return baseclass.extend({
render: function(data){
if (!data || data.trim() === '') {
return E('div', {
'class': 'cbi-section',
'style': 'color: #999; text-align: center;'
}, _('Wait devices information'));
var index = uci.sections('modeminfo', 'general');
if (!index || !index[0] || index[0].index !== "1") {
return null;
} else {
return E('div', {
'class': 'cbi-section',
'style': 'color: #999; text-align: center;'
}, _('Wait devices information'));
}
}
try {
+16 -4
View File
@@ -132,11 +132,23 @@ msgstr "Только QMI"
msgid "QMI over MBIM"
msgstr "QMI через MBIM"
msgid "Index page"
msgstr "На главную"
msgid "Short info"
msgstr "Краткая информация"
msgid "Short info on Overview page"
msgstr "Краткая информация о сети на главной странице"
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 "Показвать краткую информацию<br />На главной: показывать главной странице раздел \"Сотовая сеть\"<br />Меню: показывать в строке меню на всех страницах<br />ВНИМАНИЕ: не добавляйте слишком много устройств в строку меню, это может сломать отображение."
msgid "none"
msgstr "не отображать"
msgid "overview"
msgid "на главной"
msgid "menubar"
msgstr "в строке меню"
msgid "Wait devices information"
msgstr "Ожидаются данные устройств"
msgid "sec"
msgstr "сек"
@@ -8,7 +8,8 @@
},
"file": {
"/usr/bin/modeminfo": [ "exec" ],
"/usr/share/modeminfo/scripts/rmtmp": [ "exec" ]
"/usr/share/modeminfo/scripts/rmtmp": [ "exec" ],
"/sbin/uci": [ "exec" ]
},
"uci": [ "modeminfo" ]
},
+1 -14
View File
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=26.5.11
PKG_RELEASE:=126
PKG_RELEASE:=127
PKG_PO_VERSION:=$(PKG_VERSION)
PKG_CONFIG_DEPENDS:= \
@@ -18,8 +18,6 @@ PKG_CONFIG_DEPENDS:= \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy \
CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria \
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_Server \
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_Hysteria:hysteria \
+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_Server:shadowsocks-rust-ssserver \
+PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client:shadowsocksr-libev-ssr-local \
@@ -106,14 +101,6 @@ config PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy
select PACKAGE_naiveproxy
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
bool "Include Shadowsocks Rust Client"
depends on !i386
@@ -1,7 +1,6 @@
local api = require "luci.passwall.api"
local uci = api.uci
local appname = "passwall"
local has_ss = api.is_finded("ss-redir")
local has_ss_rust = api.is_finded("sslocal")
local has_singbox = api.finded_com("sing-box")
local has_xray = api.finded_com("xray")
@@ -12,10 +11,6 @@ local vmess_type = {}
local vless_type = {}
local hysteria2_type = {}
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
local s = "shadowsocks-rust"
table.insert(ss_type, s)
@@ -16,7 +16,6 @@ end
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_singbox = api.finded_com("sing-box")
local has_xray = api.finded_com("xray")
@@ -27,10 +26,6 @@ local vmess_type = {}
local vless_type = {}
local hysteria2_type = {}
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
local s = "shadowsocks-rust"
table.insert(ss_type, s)
@@ -262,6 +262,13 @@ if load_balancing_options then -- [[ Load balancing Start ]]
o.default = "2"
o.placeholder = "2"
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 ]]
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_version = api.get_app_version("sing-box"):match("[^v]+")
o = s:option(ListValue, _n("protocol"), translate("Protocol"))
o:value("socks", "Socks")
o:value("http", "HTTP")
@@ -351,13 +353,28 @@ end
if singbox_tags:find("with_quic") then
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: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.datatype = "or(uinteger,portrange)"
o.placeholder = "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.password = true
@@ -479,7 +496,7 @@ o.default = "0"
o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false })
o:depends({ [_n("protocol")] = "tuic" })
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 = s:option(TextValue, _n("ech_config"), translate("ECH Config"))
@@ -787,10 +804,14 @@ local protocols = s.fields[_n("protocol")].keylist
if #protocols > 0 then
for i, v in ipairs(protocols) do
if not v:find("^_") then
s.fields[_n("address")]:depends({ [_n("protocol")] = v })
s.fields[_n("port")]:depends({ [_n("protocol")] = v })
s.fields[_n("domain_resolver")]:depends({ [_n("protocol")] = v })
s.fields[_n("domain_strategy")]:depends({ [_n("protocol")] = v })
local depends_condition = { [_n("protocol")] = v }
if v == "hysteria2" then
depends_condition[_n("hysteria2_realms")] = false
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
@@ -805,6 +826,7 @@ if not load_shunt_options then
o:value("1", translate("Preproxy Node"))
end
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: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.datatype = "port"
o.rmempty = false
o:depends({ [_n("custom")] = false })
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.datatype = "port"
o.rmempty = false
o:depends({ [_n("custom")] = false })
o = s:option(Flag, _n("auth"), translate("Auth"))
@@ -154,6 +155,17 @@ if singbox_tags:find("with_quic") then
end
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.password = true
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")] = "hysteria" })
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.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
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
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
local protocol = e.protocol
if protocol == "vmess" then
@@ -501,10 +502,11 @@ function get_valid_nodes()
type_name = type_name .. " " .. protocol
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})
if show_node_info == "1" then
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})
end
end
@@ -596,6 +598,9 @@ function get_node_remarks(n)
end
type_name = type_name .. " " .. protocol
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})
end
end
@@ -1595,3 +1600,29 @@ function vps_domain_exclude(domain)
if map[domain] then return true end
return false
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
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
config = jsonc.parse(api.base64Decode(user.config_str))
else
@@ -67,11 +67,7 @@ function gen_config(var)
server_host = api.get_ipv6_full(server_host)
end
local server = server_host .. (server_port and ":" .. server_port or "")
if node.hysteria2_hop then
server = server .. (server_port and "," or ":") .. string.gsub(node.hysteria2_hop, ":", "-")
end
local server = server_host .. ":" .. ((server_port or "") .. "," .. (node.hysteria2_hop or "")):gsub("^[%s,]+", ""):gsub("[%s,]+$", ""):gsub(":", "-")
local config = {
server = (function()
@@ -80,11 +80,7 @@ function gen_config(var)
tcp_tproxy = var["tcp_tproxy"] and true or nil
}
if node.type == "SS" 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
if node.type == "SSR" then
config.protocol = node.protocol
config.protocol_param = node.protocol_param
config.obfs = node.obfs
@@ -103,8 +103,8 @@ function gen_outbound(flag, node, tag, proxy_table)
local run_socks_instance = true
if proxy_table ~= nil and type(proxy_table) == "table" then
proxy_tag = proxy_table.tag or nil
fragment = (proxy_table.fragment and node.protocol ~= "naive") and true or nil
record_fragment = (proxy_table.record_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 not node.hysteria2_realms) and true or nil
run_socks_instance = proxy_table.run_socks_instance
end
@@ -472,6 +472,7 @@ function gen_outbound(flag, node, tag, proxy_table)
table.insert(server_ports, range)
end
end
result.server_port = nil
end
protocol_table = {
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)
end
end
result.server_port = nil
end
if node.hysteria2_realms then
server_ports = {}
end
local interval, interval_max
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
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
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
@@ -908,7 +924,17 @@ function gen_config_server(node)
}
},
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
@@ -2088,7 +2114,7 @@ function gen_config(var)
if not value.detour and value.server then
value.detour = "direct"
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
end
for k, v in pairs(config.outbounds[index]) do
@@ -1072,7 +1072,13 @@ function gen_config(var)
type = _node.balancingStrategy,
settings = {
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
@@ -4,13 +4,12 @@ local api = require "luci.passwall.api"
local appname = api.appname
local map = self.map
local has_ss = api.is_finded("ss-redir")
local has_ss_rust = api.is_finded("sslocal")
local has_singbox = api.finded_com("sing-box")
local has_xray = api.finded_com("xray")
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 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"}})
@@ -165,10 +164,7 @@ local current_node = map:get(section)
var dom_prefix = null
var protocol = ""
if (v_type === "SS") {
dom_prefix = "ss_"
protocol = "ss"
} else if (v_type === "SS-Rust") {
if (v_type === "SS-Rust") {
dom_prefix = "ssrust_"
protocol = "ss"
} else if (v_type === "SSR") {
@@ -196,7 +192,7 @@ local current_node = map:get(section)
}
}
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"
var v_port = opt.get(dom_prefix + "port");
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_"
opt.set('type', "sing-box");
opt.set(dom_prefix + 'protocol', "shadowsocks");
} else if (ss_type == "shadowsocks-rust") {
} else {
dom_prefix = "ssrust_"
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();
@@ -910,7 +910,11 @@ table td, .table .td {
} else {
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;
}
@@ -952,7 +956,7 @@ table td, .table .td {
var newDom = node_template.cloneNode(true);
newDom.classList.add("cbi-rowstyle-" + (i % 2 + 1));
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>');
} else {
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 {
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>');
} 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>');
@@ -971,11 +976,13 @@ table td, .table .td {
innerHTML = innerHTML.split("{{group}}").join(o["group"] || "");
let node_remarks = get_remarks_name(o);
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"]
if (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);
+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."
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"
msgstr "分流"
@@ -374,7 +374,7 @@ run_socks() {
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
fi
@@ -463,17 +463,6 @@ run_socks() {
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
;;
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)
[ "$http_port" != "0" ] && {
http_flag=1
@@ -632,15 +621,6 @@ run_redir() {
lua $UTIL_SS gen_config "$(json_dump)" > $config_file
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)
local plugin_sh="${config_file%.json}_plugin.sh"
json_add_string "plugin_sh" "$plugin_sh"
@@ -889,23 +869,6 @@ run_redir() {
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}
;;
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)
json_add_string "local_tcp_redir_port" "$local_port"
[ "${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)
DNS_REDIRECT=$(config_t_get global dns_redirect 1)
REDIRECT_LIST="socks ss ss-rust ssr sing-box xray naiveproxy hysteria2"
TPROXY_LIST="socks ss ss-rust ssr sing-box xray hysteria2"
REDIRECT_LIST="socks ss-rust ssr sing-box xray naiveproxy hysteria2"
TPROXY_LIST="socks ss-rust ssr sing-box xray hysteria2"
NEXT_DNS_LISTEN_PORT=15353
TUN_DNS="127.0.0.1#${NEXT_DNS_LISTEN_PORT}"
@@ -24,7 +24,6 @@ local UrlDecode = api.UrlDecode
local uci = api.uci
local fs = api.fs
local has_ss = api.is_finded("ss-redir")
local has_ss_rust = api.is_finded("sslocal")
local has_ssr = api.is_finded("ssr-local") and api.is_finded("ssr-redir")
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_KEEP_LIST = uci:get(appname, "@global_subscribe[0]", "filter_keep_list") or {}
-- 取节点使用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_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"}})
@@ -43,7 +42,6 @@ local DEFAULT_HYSTERIA2_TYPE = api.get_core("hysteria2_type", {{has_hysteria2,"h
local core_has = {
["xray"] = has_xray,
["sing-box"] = has_singbox,
["shadowsocks-libev"] = has_ss,
["shadowsocks-rust"] = has_ss_rust,
["hysteria2"] = has_hysteria2
}
@@ -482,9 +480,7 @@ end
-- 设置 ss 协议实现类型
local function set_ss_implementation(ss_type, result)
if ss_type == "shadowsocks-libev" and has_ss then
result.type = "SS"
elseif ss_type == "shadowsocks-rust" and has_ss_rust then
if ss_type == "shadowsocks-rust" and has_ss_rust then
result.type = 'SS-Rust'
elseif ss_type == "xray" and has_xray then
result.type = 'Xray'
@@ -835,20 +831,6 @@ local function processData(szType, content, add_mode, group, sub_cfg)
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
params.type = string.lower(params.type)
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
result.transport = params.type
end
if result.type ~= "SS-Rust" and result.type ~= "SS" then
if result.type ~= "SS-Rust" then
if params.type == 'ws' then
result.ws_host = params.host
result.ws_path = params.path
+2 -2
View File
@@ -6,8 +6,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall2
PKG_VERSION:=26.5.1
PKG_RELEASE:=43
PKG_VERSION:=26.5.18
PKG_RELEASE:=44
PKG_PO_VERSION:=$(PKG_VERSION)
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, "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, "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 ]]
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
uci:delete(appname, t[".name"])
end
local changed = false
local auto_switch_node_list = uci:get(appname, t[".name"], "autoswitch_backup_node") or {}
for i = #auto_switch_node_list, 1, -1 do
if w == auto_switch_node_list[i] then
table.remove(auto_switch_node_list, i)
changed = true
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)
uci:foreach(appname, "haproxy_config", function(t)
if t["lbss"] == w then
@@ -21,27 +21,50 @@ local function _n(name)
end
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.datatype = "port"
o:depends({ [_n("realms")] = false })
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.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.datatype = "or(uinteger,portrange)"
o.placeholder = "30"
o.default = "30"
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 = 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.password = true
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.default = "0"
@@ -164,28 +164,66 @@ if load_balancing_options then -- [[ Load balancing Start ]]
-- Fallback Node
o = s:option(ListValue, _n("fallback_node"), translate("Fallback Node"))
o.group = {"",""}
o:value("", translate("Close(Not use)"))
o:value("_direct", translate("Direct Connection"))
o:depends({ [_n("protocol")] = "_balancing" })
o.template = appname .. "/cbi/nodes_listvalue"
o.group = {""}
local function check_fallback_chain(fb)
for k, v in pairs(fallback_list) do
if v.fallback == fb then
fallback_list[k] = nil
check_fallback_chain(v.id)
-- Maximum number of fallback nesting layers
local MAX_FALLBACK_DEPTH = 3
-- Check if a loop will form.
local function will_loop(start_id, target_id, depth)
depth = depth or 0
-- 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
return false
end
-- Check the fallback chain and remove the balancer node that would form a closed loop.
if is_balancer then
check_fallback_chain(arg[1])
-- Get fallback chain depth
local function get_fallback_depth(id, depth)
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
for i, v in ipairs(fallback_list) do
o:value(v.id, v.remark)
o.group[#o.group+1] = (v.group and v.group ~= "") and v.group or translate("default")
for _, v in ipairs(fallback_list) do
local depth = get_fallback_depth(v.id)
-- 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
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
o:value(v.id, v.remark)
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.placeholder = "2"
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 ]]
if load_iface_options then -- [[ Custom Interface Start ]]
@@ -299,11 +344,9 @@ o.placeholder = "30"
o.default = "30"
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps"))
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_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:value("", translate("Disable"))
@@ -313,9 +356,11 @@ 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(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o.password = true
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps"))
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"})
@@ -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_version = api.get_app_version("sing-box"):match("[^v]+")
o = s:option(ListValue, _n("protocol"), translate("Protocol"))
o:value("socks", "Socks")
o:value("http", "HTTP")
@@ -388,19 +390,32 @@ end
if singbox_tags:find("with_quic") then
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: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.placeholder = "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:depends({ [_n("protocol")] = "hysteria2" })
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_down_mbps"), translate("Max download Mbps"))
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.password = true
o:depends({ [_n("protocol")] = "hysteria2"})
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
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:depends({ [_n("hysteria2_obfs_type")] = "salamander" })
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
o.password = true
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps"))
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"})
end
@@ -506,7 +533,7 @@ o.default = "0"
o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false })
o:depends({ [_n("protocol")] = "tuic" })
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 = s:option(TextValue, _n("ech_config"), translate("ECH Config"))
@@ -818,10 +845,14 @@ local protocols = s.fields[_n("protocol")].keylist
if #protocols > 0 then
for i, v in ipairs(protocols) do
if not v:find("^_") then
s.fields[_n("address")]:depends({ [_n("protocol")] = v })
s.fields[_n("port")]:depends({ [_n("protocol")] = v })
s.fields[_n("domain_resolver")]:depends({ [_n("protocol")] = v })
s.fields[_n("domain_strategy")]:depends({ [_n("protocol")] = v })
local depends_condition = { [_n("protocol")] = v }
if v == "hysteria2" then
depends_condition[_n("hysteria2_realms")] = false
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
@@ -834,16 +865,21 @@ if not load_shunt_options then
if not (load_iface_options or load_urltest_options) then
-- Special node cannot be use pre-proxy.
o:value("1", translate("Preproxy Node"))
o:value("3", translate("Outbound Interface"))
end
o:value("2", translate("Landing Node"))
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.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:depends({ [_n("chain_proxy")] = "2" })
o2:depends({ [_n("chain_proxy")] = "2", [_n("hysteria2_realms")] = false })
o2.template = appname .. "/cbi/nodes_listvalue"
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.datatype = "port"
o.rmempty = 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: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.password = true
o.rewrite_option = o.option
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.default = "1"
o.rewrite_option = o.option
@@ -129,6 +129,14 @@ 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: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.default = 0
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: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 ]]
o = s:option(Flag, _n("tls"), translate("TLS"))
o.default = 0
@@ -58,6 +58,7 @@ o:depends({ [_n("custom")] = false })
o = s:option(Value, _n("port"), translate("Listen Port"))
o.datatype = "port"
o.rmempty = false
o:depends({ [_n("custom")] = false })
o = s:option(Flag, _n("auth"), translate("Auth"))
@@ -157,10 +158,29 @@ if singbox_tags:find("with_quic") then
end
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.password = true
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.default = 0
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: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
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")] = "hysteria" })
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.default = ""
@@ -31,7 +31,11 @@ local type_table = {}
for filename in fs.dir(types_dir) do
table.insert(type_table, filename)
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
local p_func = loadfile(types_dir .. value)
+40 -5
View File
@@ -377,11 +377,13 @@ function is_special_node(e)
end
function is_ip(val)
val = trim(val):lower()
local str = val:match("%[(.-)%]") or val
return datatypes.ipaddr(str) or false
end
function is_ipv6(val)
val = trim(val):lower()
local str = val:match("%[(.-)%]") or val
return datatypes.ip6addr(str) or false
end
@@ -497,9 +499,10 @@ function get_valid_nodes()
end
end
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
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
local protocol = e.protocol
if protocol == "vmess" then
@@ -526,10 +529,13 @@ function get_valid_nodes()
type_name = type_name .. " " .. protocol
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})
if show_node_info == "1" then
port = port:gsub(":", "-")
e["remark"] = trim("%s:[%s] %s:%s" % {type_name, e.remarks, address, port})
port = (port or ""):gsub(":", "-")
if not is_realm then
e["remark"] = trim("%s:[%s] %s:%s" % {type_name, e.remarks, address, port})
end
end
e.node_type = "normal"
if not e.group or e.group == "" then
@@ -619,6 +625,9 @@ function get_node_remarks(n)
end
type_name = type_name .. " " .. protocol
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})
end
end
@@ -1204,7 +1213,7 @@ function to_move(app_name,file)
}
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
sys.call("/etc/init.d/passwall2 stop")
end
@@ -1588,3 +1597,29 @@ function get_dnsmasq_server_domain()
end
return dnsmasq_server_t
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)
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 = {
cert = node.tls_certificateFile,
key = node.tls_keyFile,
},
obfs = (node.hysteria2_obfs) and {
obfs = (node.hysteria2_obfs_type and node.hysteria2_obfs_password) and {
type = "salamander",
salamander = {
password = node.hysteria2_obfs
password = node.hysteria2_obfs_password
}
} or nil,
auth = {
@@ -26,6 +35,9 @@ function gen_config_server(node)
} or nil,
ignoreClientBandwidth = (node.hysteria2_ignoreClientBandwidth == "1") 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
end
@@ -51,14 +63,19 @@ function gen_config(var)
if api.is_ipv6(server_host) then
server_host = api.get_ipv6_full(server_host)
end
local server = server_host .. ":" .. server_port
if (node.hysteria2_hop) then
server = server .. "," .. string.gsub(node.hysteria2_hop, ":", "-")
end
local server = server_host .. ":" .. ((server_port or "") .. "," .. (node.hysteria2_hop or "")):gsub("^[%s,]+", ""):gsub("[%s,]+$", ""):gsub(":", "-")
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 = {
type = "udp",
udp = node.hysteria2_hop and (function()
@@ -83,10 +100,10 @@ function gen_config(var)
return udp
end)() or nil
},
obfs = (node.hysteria2_obfs) and {
obfs = (node.hysteria2_obfs_type and node.hysteria2_obfs_password) and {
type = "salamander",
salamander = {
password = node.hysteria2_obfs
password = node.hysteria2_obfs_password
}
} or nil,
auth = node.hysteria2_auth_password,
@@ -134,8 +134,8 @@ function gen_outbound(flag, node, tag, proxy_table)
local run_socks_instance = true
if proxy_table ~= nil and type(proxy_table) == "table" then
proxy_tag = proxy_table.tag or nil
fragment = proxy_table.fragment or nil
record_fragment = proxy_table.record_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 and node.protocol ~= "naive" and not node.hysteria2_realms) and true or nil
run_socks_instance = proxy_table.run_socks_instance
end
@@ -250,7 +250,7 @@ function gen_outbound(flag, node, tag, proxy_table)
enabled = true,
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.
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.
--max_version = "1.3",
fragment = fragment,
@@ -506,6 +506,7 @@ function gen_outbound(flag, node, tag, proxy_table)
table.insert(server_ports, range)
end
end
result.server_port = nil
end
protocol_table = {
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,
auth = (node.hysteria_auth_type == "base64") and node.hysteria_auth_password or nil,
auth_str = (node.hysteria_auth_type == "string") and node.hysteria_auth_password or nil,
recv_window_conn = tonumber(node.hysteria_recv_window_conn),
recv_window = tonumber(node.hysteria_recv_window),
disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false,
recv_window_conn = tonumber(node.hysteria_recv_window_conn), --1.14 将变更为 stream_receive_window
recv_window = tonumber(node.hysteria_recv_window), --1.14 将变更为 connection_receive_window
disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false, --1.14 将变更为 disable_path_mtu_discovery
tls = tls
}
end
@@ -545,13 +546,12 @@ function gen_outbound(flag, node, tag, proxy_table)
heartbeat = (tonumber(node.tuic_heartbeat) or 3) .. "s",
tls = tls
}
if node.tuic_alpn and node.tuic_alpn ~= "default" then
local alpn = {}
string.gsub(node.tuic_alpn, '[^,]+', function(w)
table.insert(alpn, w)
end)
if #alpn > 0 then protocol_table.tls.alpn = alpn end
end
node.tuic_alpn = (node.tuic_alpn and node.tuic_alpn ~= "default") and node.tuic_alpn or "h3"
local alpn = {}
string.gsub(node.tuic_alpn, '[^,]+', function(w)
table.insert(alpn, w)
end)
if #alpn > 0 then protocol_table.tls.alpn = alpn end
end
if node.protocol == "hysteria2" then
@@ -563,6 +563,10 @@ function gen_outbound(flag, node, tag, proxy_table)
table.insert(server_ports, range)
end
end
result.server_port = nil
end
if node.hysteria2_realms then
server_ports = {}
end
local interval, interval_max
if next(server_ports) then
@@ -595,7 +599,29 @@ function gen_outbound(flag, node, tag, proxy_table)
password = node.hysteria2_obfs_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
@@ -881,10 +907,10 @@ function gen_config_server(node)
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_client = node.hysteria_recv_window_client and tonumber(node.hysteria_recv_window_client) or nil,
max_conn_client = node.hysteria_max_conn_client and tonumber(node.hysteria_max_conn_client) or nil,
disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false,
recv_window_conn = node.hysteria_recv_window_conn and tonumber(node.hysteria_recv_window_conn) or nil, --1.14 to stream_receive_window
recv_window_client = node.hysteria_recv_window_client and tonumber(node.hysteria_recv_window_client) or nil, --1.14 to connection_receive_window
max_conn_client = node.hysteria_max_conn_client and tonumber(node.hysteria_max_conn_client) or nil, --1.14 to max_concurrent_streams
disable_mtu_discovery = (node.hysteria_disable_mtu_discovery == "1") and true or false, --1.14 to disable_path_mtu_discover
tls = tls
}
end
@@ -928,7 +954,17 @@ function gen_config_server(node)
}
},
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
@@ -1312,7 +1348,6 @@ function gen_config(var)
else
ut_nodes = _node.urltest_node
end
if #ut_nodes == 0 then return nil end
local valid_nodes = {}
for i = 1, #(ut_nodes or {}) do
local ut_node_id = ut_nodes[i]
@@ -1444,6 +1479,10 @@ function gen_config(var)
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
end
@@ -1775,6 +1814,10 @@ function gen_config(var)
tag = "local"
})
route.default_domain_resolver = {
server = "local"
}
if direct_dns_udp_server then
table.insert(dns.servers, {
tag = "direct",
@@ -2051,7 +2094,7 @@ function gen_config(var)
string.gsub(direct_ipset, '[^' .. "," .. ']+', function(w)
sys.call("ipset -q -F " .. w)
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}'")
string.gsub(ipset_list, '[^' .. "\r\n" .. ']+', function(w)
sys.call("ipset -q -F " .. w)
@@ -2070,7 +2113,7 @@ function gen_config(var)
end)
local family = "inet"
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}'")
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))
@@ -2126,19 +2169,15 @@ function gen_config(var)
type = "direct",
tag = "direct",
routing_mark = 255,
domain_resolver = {
server = "local",
strategy = "prefer_ipv6"
}
})
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
sys.call(string.format("echo '%s' >> %s", value["_id"], api.TMP_PATH .. "/direct_node_list"))
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"
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
end
for k, v in pairs(config.outbounds[index]) do
@@ -2290,10 +2329,6 @@ function gen_front_dns_config(var)
type = "direct",
tag = "direct",
routing_mark = 255,
domain_resolver = {
server = "direct",
strategy = direct_strategy
}
})
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
})
end
route.default_domain_resolver = {
server = "direct",
strategy = direct_strategy
}
end
if default_dns_udp_server then
@@ -794,6 +794,9 @@ function gen_config_server(node)
config.outbounds[index][k] = nil
end
end
if value.protocol == "freedom" and api.compare_versions(xray_version, "<", "26.5.3") then -- Todo is to remove it
value.settings = nil
end
end
return config
@@ -1049,10 +1052,10 @@ function gen_config(var)
if #valid_nodes == 0 then return nil end
-- fallback node
local fallback_node_tag = nil
local fallback_node_id = _node.fallback_node
if not fallback_node_id or fallback_node_id == "" then fallback_node_id = nil end
if fallback_node_id then
fallback_node_id = (fallback_node_id and fallback_node_id ~= "") and fallback_node_id or nil
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
for _, outbound in ipairs(outbounds) do
if string.sub(outbound.tag, 1, #fallback_node_id) == fallback_node_id then
@@ -1082,7 +1085,13 @@ function gen_config(var)
type = _node.balancingStrategy,
settings = {
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
@@ -1264,9 +1273,9 @@ function gen_config(var)
interface = node.iface
}
},
settings = {
settings = (api.compare_versions(xray_version, ">", "26.4.25")) and { -- Todo: Remove version check
finalRules = {{ action = "allow" }}
}
} or nil
}
sys.call(string.format("mkdir -p %s && touch %s/%s", api.TMP_IFACE_PATH, api.TMP_IFACE_PATH, node.iface))
end
@@ -1763,15 +1772,22 @@ function gen_config(var)
end
end
end
local dns_block_mode = "host"
dns_block_mode = ""
if dns_block_mode == "host" and dns_outboundTag == "blackhole" then
for d_i, d_k in ipairs(value.domain) do
dns.hosts[d_k] = "0.0.0.0"
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.
if dns_outboundTag == "blackhole" then
if dns_block then
local dns_block_mode = "Hosts" -- use the Hosts mode to block.
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
dns_server = nil
end
if dns_server then
dns_server.finalQuery = true
dns_server.domains = value.domain
if value.shunt_rule_name then
dns_server.tag = "dns-in-" .. value.shunt_rule_name
@@ -1806,7 +1822,7 @@ function gen_config(var)
string.gsub(direct_ipset, '[^' .. "," .. ']+', function(w)
sys.call("ipset -q -F " .. w)
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}'")
string.gsub(ipset_list, '[^' .. "\r\n" .. ']+', function(w)
sys.call("ipset -q -F " .. w)
@@ -1825,7 +1841,7 @@ function gen_config(var)
end)
local family = "inet"
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}'")
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))
@@ -2080,7 +2096,12 @@ function gen_proto_config(var)
end
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 = {
@@ -35,21 +35,47 @@ local appname = api.appname
</style>
<script type="text/javascript">
//<![CDATA[
var shunt_list = JSON.parse('<%=self.shunt_list%>');
const shunt_list = JSON.parse('<%=self.shunt_list%>');
function go() {
var _status = document.getElementsByClassName('_status');
for (var i = 0; i < _status.length; i++) {
var id = _status[i].getAttribute("socks_id");
function to_edit_node(btn) {
if (!btn) return;
const idReg = /^cbid\..*\..*node$/;
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")%>', {
index: i,
id: id
},
function(x, result) {
var index = result.index;
var div = '';
var div1 = '<font style="font-weight:bold;" color="green">✓</font>&nbsp';
var div2 = '<font style="font-weight:bold;" color="red">X</font>&nbsp';
const index = result.index;
let div = '';
const div1 = '<font style="font-weight:bold;" color="green">✓</font>&nbsp';
const div2 = '<font style="font-weight:bold;" color="red">X</font>&nbsp';
if (result.socks_status) {
div += div1;
@@ -68,82 +94,111 @@ local appname = api.appname
);
}
var global_id = null;
var global = document.getElementById("cbi-<%=appname%>-global");
let global_id = null;
const global = document.getElementById("cbi-<%=appname%>-global");
if (global) {
var node = global.getElementsByClassName("cbi-section-node")[0];
var node_id = node.getAttribute("id");
global_id = node_id;
var all_node = node.querySelectorAll("[id]");
var reg1 = /^cbid\..*node\.main$/;
// Add an "Edit" placeholder to the routing rule node in advance to ensure left alignment of the node list.
document.querySelectorAll('.td[data-widget="nodes_listvalue"][data-name="_node"] > div').forEach(container => {
if (!container.querySelector('.shunt-node-placeholder')) {
const placeholder = document.createElement('div');
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++) {
var el = all_node[i];
await delay(100);
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;
var node_select = el;
const node_select = el;
if (!node_select) continue;
var cbid = el.id.replace(/\.main$/, "");
var hidden_select = document.getElementById(cbid);
var node_select_value = hidden_select ? hidden_select.options[0].value : "";
const cbid = el.id.replace(/\.main$/, "");
const hidden_select = document.getElementById(cbid);
const node_select_value = hidden_select?.options[0]?.value;
if (!node_select_value || node_select_value.indexOf("_default") === 0 || node_select_value.indexOf("_direct") === 0 || node_select_value.indexOf("_blackhole") === 0) {
continue;
}
var 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);
}
var html = '<a href="#" onclick="location.href=\'' + to_url + '\'"><%:Edit%></a>';
let html = '<a href="#" onclick="return to_edit_node(this);"><%:Edit%></a>';
var m = cbid.match(/\.node$/);
const m = cbid.match(/\.node$/);
if (m) {
html += '<a href="#" onclick="window.open(\'' + '<%=api.url("get_redir_log")%>?id=default&name=' + 'global' + '\', \'_blank\')"><%:Log%></a>';
}
node_select.insertAdjacentHTML("beforeend",
'<div class="node-actions" style="display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; margin-left:4px;">'
+ html + '</div>'
);
html = '<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) {
var socks_enabled_dom = document.getElementById(global_id + "-socks_enabled");
socks_enabled_dom.parentNode.removeChild(socks_enabled_dom);
var descr = socks.getElementsByClassName("cbi-section-descr")[0];
descr.outerHTML = socks_enabled_dom.outerHTML;
rows = socks.getElementsByClassName("cbi-section-table-row");
for (var i = 0; i < rows.length; i++) {
const socks_enabled_dom = document.getElementById(global_id + "-socks_enabled");
const descr = socks.getElementsByClassName("cbi-section-descr")[0];
descr.replaceWith(socks_enabled_dom);
const rows = socks.getElementsByClassName("cbi-section-table-row");
for (let i = 0; i < rows.length; i++) {
try {
var row = rows[i];
var id = row.id;
const row = rows[i];
const id = row.id;
if (!id) continue;
var dom_id = id + "-node";
var cbid = dom_id.replace("cbi-", "cbid-").replace(new RegExp("-", 'g'), ".");
let dom_id = id + "-node";
const cbid = dom_id.replace("cbi-", "cbid-").replace(new RegExp("-", 'g'), ".");
dom_id = cbid + ".main";
var node_select = document.getElementById(dom_id);
const node_select = document.getElementById(dom_id);
if (!node_select) continue;
var hidden_select = document.getElementById(cbid);;
var node_select_value = hidden_select ? hidden_select.options[0].value : "";
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>';
let html = '<a href="#" onclick="return to_edit_node(this);"><%: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",
'<div class="node-actions" style="display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; margin-left:4px;">'
+ html + '</div>'
);
}
node_select.insertAdjacentHTML("afterend",
'<div class="node-actions" style="display:inline-flex; align-items:center; gap:4px; flex-wrap:wrap; margin-left:4px;">'
+ html + '</div>'
);
} 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(() => {
const hiddenSelect = document.getElementById("cbid.<%=appname%>.<%=self.global_cfgid%>.node");
let o_val = hiddenSelect.value
@@ -184,8 +239,6 @@ local appname = api.appname
shunt_taboption.appendChild(shunt_option_list);
}
}
go();
}, 100));
//setTimeout("go()", 1000);
//]]>
</script>
@@ -602,10 +602,10 @@ local current_node = map:get(section)
if (v_type === "Hysteria2") {
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 != "") {
params += "&obfs=" + "salamander";
params += opt.query("obfs-password", "hysteria2_obfs");
params += opt.query("obfs-password", "hysteria2_obfs_password");
}
params += opt.query("mport", "hysteria2_hop");
} else {
@@ -1621,7 +1621,8 @@ local current_node = map:get(section)
dom_prefix = "hysteria2_"
opt.set(dom_prefix + 'auth_password', decodeURIComponent(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 || "");
}
@@ -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_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_disable_sni', queryParam.disable_sni === "1");
opt.set(
@@ -875,6 +875,8 @@ table td, .table .td {
p = "URLTest";
} else if (protocol == "_shunt") {
p = "<%:Shunt%>";
} else if (protocol == "_iface") {
p = "<%:Interface%>";
} else if (protocol == "vmess") {
p = "VMess";
} else if (protocol == "vless") {
@@ -911,7 +913,11 @@ table td, .table .td {
} else {
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;
}
@@ -953,7 +959,7 @@ table td, .table .td {
var newDom = node_template.cloneNode(true);
newDom.classList.add("cbi-rowstyle-" + (i % 2 + 1));
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>');
} else {
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"] || "");
let node_remarks = get_remarks_name(o);
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"]
if (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);
+11 -2
View File
@@ -46,8 +46,8 @@ msgstr "لیست قوانین"
msgid "Access control"
msgstr "کنترل دسترسی"
msgid "Watch Logs"
msgstr "مشاهده گزارش‌ها"
msgid "Runtime Logs"
msgstr "گزارش‌های زمان اجرا"
msgid "Node Config"
msgstr "پیکربندی گره"
@@ -437,6 +437,12 @@ msgstr "تعداد گره‌های ترجیحی"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
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"
msgstr "شنت (Shunt)"
@@ -1782,6 +1788,9 @@ msgstr "فقط گره‌های نوع Xray یا Sing-box پشتیبانی می
msgid "Chain Proxy"
msgstr "پروکسی زنجیره‌ای"
msgid "Outbound Interface"
msgstr "رابط خروجی"
msgid "Landing Node"
msgstr "گره فرود (Landing Node)"
+11 -2
View File
@@ -46,8 +46,8 @@ msgstr "Список правил"
msgid "Access control"
msgstr "Контроль доступа"
msgid "Watch Logs"
msgstr "Просмотр журнала"
msgid "Runtime Logs"
msgstr "Журналы выполнения"
msgid "Node Config"
msgstr "Параметры узла"
@@ -433,6 +433,12 @@ msgstr "Количество предпочтительных узлов"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
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"
msgstr "Shunt (разделение трафика)"
@@ -1783,6 +1789,9 @@ msgstr "Поддерживаются только узлы типа Xray или
msgid "Chain Proxy"
msgstr "Цепочка прокси"
msgid "Outbound Interface"
msgstr "Исходящий интерфейс"
msgid "Landing Node"
msgstr "Сервер выхода"
+11 -2
View File
@@ -46,8 +46,8 @@ msgstr "规则列表"
msgid "Access control"
msgstr "访问控制"
msgid "Watch Logs"
msgstr "查看日志"
msgid "Runtime Logs"
msgstr "运行日志"
msgid "Node Config"
msgstr "节点配置"
@@ -433,6 +433,12 @@ msgstr "优选节点数量"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
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"
msgstr "分流"
@@ -1768,6 +1774,9 @@ msgstr "仅支持 Xray 或 Sing-box 类型节点。"
msgid "Chain Proxy"
msgstr "链式代理"
msgid "Outbound Interface"
msgstr "出站接口"
msgid "Landing Node"
msgstr "落地节点"
+11 -2
View File
@@ -46,8 +46,8 @@ msgstr "規則列表"
msgid "Access control"
msgstr "訪問控制"
msgid "Watch Logs"
msgstr "查看日誌"
msgid "Runtime Logs"
msgstr "運行日誌"
msgid "Node Config"
msgstr "節點配置"
@@ -433,6 +433,12 @@ msgstr "優選節點數量"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
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"
msgstr "分流"
@@ -1774,6 +1780,9 @@ msgstr "僅支援 Xray 或 Sing-box 類型節點。"
msgid "Chain Proxy"
msgstr "鏈式代理"
msgid "Outbound Interface"
msgstr "出站接口"
msgid "Landing Node"
msgstr "落地節點"
@@ -12,7 +12,7 @@
if [ "$ACTION" = "ifupdate" ]; then
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
fi
@@ -84,7 +84,7 @@ run_xray() {
json_add_string "loglevel" "${loglevel}"
[ -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}"
}
[ -n "$socks_address" ] && [ -n "$socks_port" ] && {
@@ -112,12 +112,12 @@ run_xray() {
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
if [ "${nftflag}" = "1" ]; then
local direct_nftset4="passwall2_${node}_white"
local direct_nftset6="passwall2_${node}_white6"
local direct_nftset4="psw2_${node}_white"
local direct_nftset6="psw2_${node}_white6"
local direct_nftset="4#inet#passwall2#${direct_nftset4},6#inet#passwall2#${direct_nftset6}"
else
local direct_ipset4="passwall2_${node}_white"
local direct_ipset6="passwall2_${node}_white6"
local direct_ipset4="psw2_${node}_white"
local direct_ipset6="psw2_${node}_white6"
local direct_ipset="${direct_ipset4},${direct_ipset6}"
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}
@@ -225,7 +225,7 @@ run_singbox() {
json_add_string "loglevel" "${loglevel}"
[ -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}"
}
[ -n "$socks_address" ] && [ -n "$socks_port" ] && {
@@ -251,12 +251,12 @@ run_singbox() {
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
if [ "${nftflag}" = "1" ]; then
local direct_nftset4="passwall2_${node}_white"
local direct_nftset6="passwall2_${node}_white6"
local direct_nftset4="psw2_${node}_white"
local direct_nftset6="psw2_${node}_white6"
local direct_nftset="4#inet#passwall2#${direct_nftset4},6#inet#passwall2#${direct_nftset6}"
else
local direct_ipset4="passwall2_${node}_white"
local direct_ipset6="passwall2_${node}_white6"
local direct_ipset4="psw2_${node}_white"
local direct_ipset6="psw2_${node}_white6"
local direct_ipset="${direct_ipset4},${direct_ipset6}"
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}
@@ -386,6 +386,10 @@ run_socks() {
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}" ] && {
[ "$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
@@ -555,7 +559,7 @@ socks_node_switch() {
[ -s "$pf" ] && kill -9 "$(head -n1 "$pf")" >/dev/null 2>&1
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
rm -rf "$TMP_PATH/${prefix}SOCKS_${flag}"*
done
@@ -791,7 +795,7 @@ start_socks() {
# Auto switch logic
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
}
}
@@ -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}/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
}
start_crontab() {
if [ "$ENABLED_DEFAULT_ACL" == 1 ] || [ "$ENABLED_ACLS" == 1 ]; then
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
[ -f "/tmp/lock/${CONFIG}_cron.lock" ] && {
@@ -982,11 +986,11 @@ run_ipset_chinadns_ng() {
[ -n "${ipset}" ] && {
set_names=$ipset
vps_set_names="passwall2_vps,passwall2_vps6"
vps_set_names="psw2_vps,psw2_vps6"
}
[ -n "${nftset}" ] && {
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
bind-addr 127.0.0.1
@@ -1162,7 +1166,7 @@ acl_app() {
}
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!"
stop
}
@@ -1241,8 +1245,8 @@ stop() {
kill -9 "$pid" >/dev/null 2>&1
fi
done
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 -f "sleep.*(6s|9s|58s)" | 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 XRAY_LOCATION_ASSET
unset SS_SYSTEM_DNS_RESOLVER_FORCE_BUILTIN
@@ -8,7 +8,8 @@ listen_port=$2
server_address=$3
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
}
@@ -274,7 +274,7 @@ function add_rule(var)
if address == "engage.cloudflareclient.com" then return end
if datatypes.hostname(address) then
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
process_address(t.address)
@@ -3,17 +3,17 @@
DIR="$(cd "$(dirname "$0")" && pwd)"
MY_PATH=$DIR/iptables.sh
UTILS_PATH=$DIR/utils.sh
IPSET_LOCAL="passwall2_local"
IPSET_PROXY_LAN="passwall2_proxy_lan"
IPSET_LAN="passwall2_lan"
IPSET_VPS="passwall2_vps"
IPSET_WAN="passwall2_wan"
IPSET_LOCAL="psw2_local"
IPSET_PROXY_LAN="psw2_proxy_lan"
IPSET_LAN="psw2_lan"
IPSET_VPS="psw2_vps"
IPSET_WAN="psw2_wan"
IPSET_LOCAL6="passwall2_local6"
IPSET_PROXY_LAN6="passwall2_proxy_lan6"
IPSET_LAN6="passwall2_lan6"
IPSET_VPS6="passwall2_vps6"
IPSET_WAN6="passwall2_wan6"
IPSET_LOCAL6="psw2_local6"
IPSET_PROXY_LAN6="psw2_proxy_lan6"
IPSET_LAN6="psw2_lan6"
IPSET_VPS6="psw2_vps6"
IPSET_WAN6="psw2_wan6"
FWMARK="0x50535732"
@@ -194,8 +194,8 @@ gen_shunt_list() {
for shunt_id in $shunt_ids; do
local shunt_node=$(config_n_get ${node} "${shunt_id}")
[ -n "$shunt_node" ] && {
local ipset_v4="passwall2_${node}_${shunt_id}"
local ipset_v6="passwall2_${node}_${shunt_id}6"
local ipset_v4="psw2_${node}_${shunt_id}"
local ipset_v6="psw2_${node}_${shunt_id}6"
ipset -! create $ipset_v4 nethash maxelem 1048576
ipset -! create $ipset_v6 nethash family inet6 maxelem 1048576
local outbound="redirect"
@@ -219,22 +219,14 @@ gen_shunt_list() {
}
local direct_ipset4=$(get_cache_var "node_${node}_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"
}
local direct_ipset6=$(get_cache_var "node_${node}_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"
}
[ "${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_LIST6}" ] && eval ${shunt_list6_var_name}=\"${_SHUNT_LIST6}\"
@@ -618,6 +610,8 @@ filter_direct_node_list() {
}
update_wan_sets() {
[ -z "$(command -v get_wan_ips)" ] && . "$UTILS_PATH"
local WAN_IP=$(get_wan_ips ip4)
[ -n "$WAN_IP" ] && {
ipset -F "$IPSET_WAN"
@@ -641,13 +635,13 @@ add_firewall_rule() {
ipset -! create $IPSET_LOCAL nethash maxelem 1048576
ipset -! create $IPSET_PROXY_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_LOCAL6 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_VPS6 nethash family inet6 maxelem 1048576
ipset -! create $IPSET_VPS6 iphash family inet6 maxelem 1048576
ipset -! create $IPSET_WAN6 nethash family inet6 maxelem 1048576
get_local_ips ip4 | sed "s/^/add $IPSET_LOCAL /" | ipset -! -R
@@ -1025,7 +1019,7 @@ del_firewall_rule() {
flush_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}
done
}
@@ -1105,7 +1099,7 @@ start() {
}
stop() {
[ -z "$(command -v log_i18n)" ] && . /usr/share/passwall2/utils.sh
[ -z "$(command -v log_i18n)" ] && . "$UTILS_PATH"
del_firewall_rule
destroy_ipset $IPSET_PROXY_LAN
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
cmd=$(cat ${TMP_SCRIPT_FUNC_PATH}/${filename})
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
#echo "${cmd} crashed, restarting." >> /tmp/log/passwall2.log
eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 &
@@ -4,17 +4,17 @@ DIR="$(cd "$(dirname "$0")" && pwd)"
MY_PATH=$DIR/nftables.sh
UTILS_PATH=$DIR/utils.sh
NFTABLE_NAME="inet passwall2"
NFTSET_LOCAL="passwall2_local"
NFTSET_PROXY_LAN="passwall2_proxy_lan"
NFTSET_LAN="passwall2_lan"
NFTSET_VPS="passwall2_vps"
NFTSET_WAN="passwall2_wan"
NFTSET_LOCAL="psw2_local"
NFTSET_PROXY_LAN="psw2_proxy_lan"
NFTSET_LAN="psw2_lan"
NFTSET_VPS="psw2_vps"
NFTSET_WAN="psw2_wan"
NFTSET_LOCAL6="passwall2_local6"
NFTSET_PROXY_LAN6="passwall2_proxy_lan6"
NFTSET_LAN6="passwall2_lan6"
NFTSET_VPS6="passwall2_vps6"
NFTSET_WAN6="passwall2_wan6"
NFTSET_LOCAL6="psw2_local6"
NFTSET_PROXY_LAN6="psw2_proxy_lan6"
NFTSET_LAN6="psw2_lan6"
NFTSET_VPS6="psw2_vps6"
NFTSET_WAN6="psw2_wan6"
FWMARK="0x50535732"
@@ -231,8 +231,8 @@ gen_shunt_list() {
for shunt_id in $shunt_ids; do
local shunt_node=$(config_n_get ${node} "${shunt_id}")
[ -n "$shunt_node" ] && {
local nftset_v4="passwall2_${node}_${shunt_id}"
local nftset_v6="passwall2_${node}_${shunt_id}6"
local nftset_v4="psw2_${node}_${shunt_id}"
local nftset_v6="psw2_${node}_${shunt_id}6"
gen_nftset $nftset_v4 ipv4_addr 0 0
gen_nftset $nftset_v6 ipv6_addr 0 0
local outbound="redirect"
@@ -263,14 +263,6 @@ gen_shunt_list() {
gen_nftset $direct_nftset6 ipv6_addr 0 0
_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_LIST6}" ] && eval ${shunt_list6_var_name}=\"${_SHUNT_LIST6}\"
@@ -681,6 +673,8 @@ mwan3_start() {
}
update_wan_sets() {
[ -z "$(command -v get_wan_ips)" ] && . "$UTILS_PATH"
local WAN_IP=$(get_wan_ips ip4)
[ -n "$WAN_IP" ] && {
nft flush set $NFTABLE_NAME $NFTSET_WAN
@@ -1073,7 +1067,7 @@ del_firewall_rule() {
flush_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}
done
}
@@ -1116,7 +1110,7 @@ start() {
}
stop() {
[ -z "$(command -v log_i18n)" ] && . /usr/share/passwall2/utils.sh
[ -z "$(command -v log_i18n)" ] && . "$UTILS_PATH"
del_firewall_rule
destroy_nftset $NFTSET_PROXY_LAN
destroy_nftset $NFTSET_PROXY_LAN6
@@ -6,7 +6,7 @@ APP_FILE=${APP_PATH}/app.sh
flag=0
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
done
}
@@ -63,7 +63,7 @@ test_node() {
# Kill the SS plugin process
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
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}*.*
if [ "${_proxy_status}" -eq 200 ]; then
return 0
@@ -1688,22 +1688,19 @@ local function processData(szType, content, add_mode, group, sub_cfg)
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.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
local is_singbox = sub_hysteria2_type == "sing-box" and has_singbox
result.type = is_singbox and 'sing-box' or 'Xray'
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.finalmask = (params.fm and params.fm ~= "") and api.base64Encode(params.fm) or nil
elseif has_hysteria2 then
result.type = "Hysteria2"
if params["obfs-password"] or params["obfs_password"] then
result.hysteria2_obfs = params["obfs-password"] or params["obfs_password"]
end
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"))
return nil
@@ -1761,7 +1758,7 @@ local function processData(szType, content, add_mode, group, sub_cfg)
end
result.tls_serverName = params.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_udp_relay_mode = params.udp_relay_mode or "native"
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
-- First priority: Type + Notes + IP + Port
-- First priority: Type + Notes + IP + Port + Group
if not server then
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 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
log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("First Matching node:") .. " " .. node.remarks)
end
@@ -2065,12 +2062,12 @@ local function select_node(nodes, config, parentConfig)
end
end
end
-- Second priority: Type + IP + Port
-- Second priority: Type + IP + Port + Group
if not server then
for index, node in pairs(nodes) do
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 == 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
log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Second Matching node:") .. " " .. node.remarks)
end
@@ -2081,12 +2078,12 @@ local function select_node(nodes, config, parentConfig)
end
end
end
-- Third priority: IP + Port
-- Third priority: IP + Port + Group
if not server then
for index, node in pairs(nodes) do
if config.currentNode.address and config.currentNode.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
log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Third Matching node:") .. " " .. node.remarks)
end
@@ -2097,12 +2094,12 @@ local function select_node(nodes, config, parentConfig)
end
end
end
-- Fourth priority: IP
-- Fourth priority: IP + Group
if not server then
for index, node in pairs(nodes) do
if config.currentNode.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
log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Fourth Matching node:") .. " " .. node.remarks)
end
@@ -2113,12 +2110,12 @@ local function select_node(nodes, config, parentConfig)
end
end
end
-- Fifth priority: remarks
-- Fifth priority: remarks + Group
if not server then
for index, node in pairs(nodes) do
if config.currentNode.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
log(log_level, i18n.translatef("Update [%s]", config.remarks) .. " " .. i18n.translatef("Fifth Matching node:") .. " " .. node.remarks)
end
@@ -22,7 +22,7 @@ do
stop_interval_mode=$(expr "$stop_interval_mode" \* 60)
if [ -n "$stop_week_mode" ]; then
[ "$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
@@ -31,7 +31,7 @@ do
start_interval_mode=$(expr "$start_interval_mode" \* 60)
if [ -n "$start_week_mode" ]; then
[ "$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
@@ -40,7 +40,7 @@ do
restart_interval_mode=$(expr "$restart_interval_mode" \* 60)
if [ -n "$restart_week_mode" ]; then
[ "$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
@@ -50,7 +50,7 @@ do
hourupdate=$(expr "$hourupdate" \* 60)
if [ "$autoupdate" = "1" ]; then
[ "$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
@@ -73,7 +73,7 @@ do
hour_update=$(expr "$hour_update" \* 60)
cfgids=$(echo -n $(cat ${TMP_SUB_PATH}/${name}) | sed 's# #,#g')
[ "$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
@@ -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
sleep 2s
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)
result=$(curl --connect-timeout 3 -o /dev/null -I -skL -w "%{http_code}:%{time_starttransfer}" -x $curlx "${url}")
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}*.json
local probeUrl=$(config_t_get global_other url_test_url https://www.google.com/generate_204)
result=$(curl --connect-timeout 3 --max-time 5 -o /dev/null -I -skL -w "%{http_code}:%{time_pretransfer}" -x ${curlx} "${probeUrl}")
# End the SS plugin process
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
}
@@ -412,7 +412,7 @@ run_process_queue() {
for filename in $(ls ${TMP_PROCESS_LIST_PATH}); do
cmd=$(cat ${TMP_PROCESS_LIST_PATH}/${filename})
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
eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 &
fi
+1 -1
View File
@@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-xray
PKG_VERSION:=3.7.1
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_LICENSE:=MPLv2
PKG_LICENSE_FILES:=LICENSE
@@ -261,7 +261,7 @@ function gen_config() {
const general = filter(values(config), k => k[".type"] == "general")[0] || {};
const custom_configuration_hook = loadstring(general["custom_configuration_hook"] || "return i => i;")();
return custom_configuration_hook({
let result = {
inbounds: inbounds(general, config, extra_inbound),
outbounds: outbounds(general, config, manual_tproxy, bridge, extra_inbound, fakedns),
dns: dns_conf(general, config, manual_tproxy, fakedns),
@@ -274,15 +274,19 @@ function gen_config() {
place: "holder"
} : null,
observatory: observatory(general, manual_tproxy),
reverse: {
bridges: bridges(bridge)
},
routing: {
domainStrategy: general["routing_domain_strategy"] || "AsIs",
rules: rules(general, bridge, manual_tproxy, 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());
@@ -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)
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');}}});
'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) 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
PKG_NAME:=mihomo-alpha
PKG_VERSION:=2026.05.08
PKG_RELEASE:=7
PKG_VERSION:=2026.05.16
PKG_RELEASE:=8
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/MetaCubeX/mihomo.git
PKG_SOURCE_VERSION:=35d5d4e44d7a7b8352617b1fec7faf0ba26efc8a
PKG_SOURCE_VERSION:=e9e23c4e98c34b158953f1414b317f25f3e3aa1e
PKG_MIRROR_HASH:=skip
PKG_LICENSE:=GPL3.0+
@@ -18,7 +18,7 @@ PKG_BUILD_DEPENDS:=golang/host
PKG_BUILD_PARALLEL:=1
PKG_BUILD_FLAGS:=no-mips16
PKG_BUILD_VERSION:=alpha-35d5d4e4
PKG_BUILD_VERSION:=alpha-e9e23c4e
PKG_BUILD_TIME:=$(shell date -u -Iseconds)
GO_PKG:=github.com/metacubex/mihomo
+4 -4
View File
@@ -1,14 +1,14 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=mihomo-meta
PKG_VERSION:=1.19.24
PKG_RELEASE:=4
PKG_VERSION:=1.19.25
PKG_RELEASE:=5
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_PROTO:=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_LICENSE:=GPL3.0+
@@ -18,7 +18,7 @@ PKG_BUILD_DEPENDS:=golang/host
PKG_BUILD_PARALLEL:=1
PKG_BUILD_FLAGS:=no-mips16
PKG_BUILD_VERSION:=v1.19.24
PKG_BUILD_VERSION:=v1.19.25
PKG_BUILD_TIME:=$(shell date -u -Iseconds)
GO_PKG:=github.com/metacubex/mihomo
+2 -2
View File
@@ -5,8 +5,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=mihomo
PKG_VERSION:=1.19.24
PKG_RELEASE:=8
PKG_VERSION:=1.19.25
PKG_RELEASE:=9
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
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_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
PKG_SOURCE_SUBDIR:=$(PKG_UPSTREAM_NAME)-$(PKG_UPSTREAM_VERSION)
@@ -23,7 +23,7 @@ PKG_SOURCE:=$(PKG_SOURCE_SUBDIR).tar.xz
else
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/heiher/natmap.git
PKG_SOURCE_VERSION:=079bce1d6c5798fa6f47d364b737762dae89aafc
PKG_SOURCE_VERSION:=e0975d64fbd22a34e30d573e51c44fc017b3ff8c
PKG_MIRROR_HASH:=skip
PKG_SOURCE:=$(PKG_SOURCE_SUBDIR)-$(PKG_SOURCE_VERSION).tar.gz
+2 -2
View File
@@ -5,12 +5,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=rtl8189es
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_SOURCE_URL:=https://github.com/jwrdegoede/rtl8189ES_linux.git
PKG_SOURCE_PROTO:=git
PKG_SOURCE_DATE:=2025-09-27
PKG_SOURCE_VERSION:=0a5d04114fac3c9f48a343cb905fbb6a3f9f5df5
PKG_SOURCE_VERSION:=36d917d5069f25f4c36b8b4d8b17b5d02e9116e9
PKG_MIRROR_HASH:=skip
PKG_BUILD_PARALLEL:=1
+2 -2
View File
@@ -5,8 +5,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=tuic-client
PKG_VERSION:=1.8.1
PKG_RELEASE:=4
PKG_VERSION:=1.8.2
PKG_RELEASE:=5
PKG_LICENSE_FILES:=LICENSE
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>