From f608112d083e2722c5fde7d1c173f3718f6663f9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 21 Oct 2026 14:22:54 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8F=85=20Sync=202026-10-21=2014:22:54?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gately/Makefile | 7 +- luci-app-daede/Makefile | 2 +- .../resources/view/daede/updates.js | 5 +- luci-app-daede/po/zh-cn/daede.po | 3 - luci-app-gately/Makefile | 11 - .../resources/view/gately/settings.js | 47 +-- .../resources/view/gately/status.js | 44 +-- luci-app-gately/po/templates/gately.pot | 14 +- luci-app-gately/po/zh_Hans/gately.po | 16 +- .../root/usr/share/rpcd/ucode/luci.gately | 34 +- luci-app-open-box/Makefile | 4 +- luci-app-passwall/Makefile | 9 +- luci-app-ssr-plus/Makefile | 8 +- .../model/cbi/shadowsocksr/client-config.lua | 103 +++++- .../model/cbi/shadowsocksr/server-config.lua | 68 +++- .../luasrc/model/cbi/shadowsocksr/server.lua | 2 + .../root/etc/init.d/shadowsocksr | 63 +++- .../usr/share/shadowsocksr/clash_yaml.lua | 4 +- .../share/shadowsocksr/gen_anytls_server.lua | 17 + .../usr/share/shadowsocksr/gen_config.lua | 81 ++++- .../share/shadowsocksr/gen_snell_server.lua | 13 + .../usr/share/shadowsocksr/gen_ssr_server.lua | 13 + .../share/shadowsocksr/gen_tuic_server.lua | 19 + .../root/usr/share/shadowsocksr/subscribe.lua | 9 +- luci-app-trafficctl/Makefile | 4 +- .../po/templates/luci-app-trafficctl.pot | 334 +++++++++++------- .../po/zh_Hans/luci-app-trafficctl.po | 12 +- mise/Makefile | 4 +- smartdns/Makefile | 173 +++++---- 29 files changed, 752 insertions(+), 371 deletions(-) create mode 100644 luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_anytls_server.lua create mode 100644 luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_snell_server.lua create mode 100644 luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_ssr_server.lua create mode 100644 luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_tuic_server.lua diff --git a/gately/Makefile b/gately/Makefile index 87157ae4e..40085ee00 100644 --- a/gately/Makefile +++ b/gately/Makefile @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=gately PKG_VERSION:=0.5.3 -PKG_RELEASE:=2 +PKG_RELEASE:=3 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/wangwanjie/gately/tar.gz/v$(PKG_VERSION)? @@ -20,14 +20,11 @@ PKG_BUILD_DEPENDS:=golang/host node/host node-pnpm/host PKG_BUILD_PARALLEL:=1 PKG_BUILD_FLAGS:=no-mips16 -BUILD_DATE:=$(shell date -u -d "@$(SOURCE_DATE_EPOCH)" +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date -u +"%Y-%m-%dT%H:%M:%SZ") - GO_PKG:=github.com/wangwanjie/gately GO_PKG_BUILD_PKG:=$(GO_PKG)/cmd/gately GO_PKG_LDFLAGS_X:=\ $(GO_PKG)/internal/version.Version=v$(PKG_VERSION) \ - $(GO_PKG)/internal/version.Commit=$(PKG_VERSION) \ - $(GO_PKG)/internal/version.BuildDate=$(BUILD_DATE) + $(GO_PKG)/internal/version.Commit=$(PKG_VERSION) include $(INCLUDE_DIR)/package.mk include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk diff --git a/luci-app-daede/Makefile b/luci-app-daede/Makefile index 04da08677..9e4121816 100644 --- a/luci-app-daede/Makefile +++ b/luci-app-daede/Makefile @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-daede PKG_VERSION:=1.15 -PKG_RELEASE:=53 +PKG_RELEASE:=54 PKG_MAINTAINER:=kenzok8 PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME) diff --git a/luci-app-daede/htdocs/luci-static/resources/view/daede/updates.js b/luci-app-daede/htdocs/luci-static/resources/view/daede/updates.js index f4df1c52e..c144f24e6 100644 --- a/luci-app-daede/htdocs/luci-static/resources/view/daede/updates.js +++ b/luci-app-daede/htdocs/luci-static/resources/view/daede/updates.js @@ -425,14 +425,11 @@ return view.extend({ btn.disabled = true; } else if (updatable) { meta = _('installed') + ': ' + entry.r.installed + ' → ' + _('latest') + ': ' + entry.r.latest; - } else if (cmp < 0) { - meta = _('installed') + ': ' + entry.r.installed + ' · ' + _('feed version older: %s').format(entry.r.latest); - btn.disabled = true; } else { meta = _('installed') + ': ' + entry.r.installed + ' · ' + _('up to date'); btn.disabled = true; } - const uncertain = entry.r.installed && (indexState !== 'success' || !entry.r.latest || cmp < 0); + const uncertain = entry.r.installed && (indexState !== 'success' || !entry.r.latest); pkgBody.appendChild(mkRow( updatable ? '↑' : (uncertain ? '⚠' : (entry.r.installed ? '✓' : '✗')), updatable ? 'dd-up-new' : (uncertain ? 'dd-up-warn' : (entry.r.installed ? 'dd-up-ok' : 'dd-up-err')), diff --git a/luci-app-daede/po/zh-cn/daede.po b/luci-app-daede/po/zh-cn/daede.po index 82648e1f8..dfcbc528b 100644 --- a/luci-app-daede/po/zh-cn/daede.po +++ b/luci-app-daede/po/zh-cn/daede.po @@ -329,9 +329,6 @@ msgstr "正在刷新软件源" msgid "package not found in configured feeds" msgstr "当前软件源中没有此包" -msgid "feed version older: %s" -msgstr "软件源版本较旧:%s" - msgid "Unavailable" msgstr "不可用" diff --git a/luci-app-gately/Makefile b/luci-app-gately/Makefile index 14180c152..af914890c 100644 --- a/luci-app-gately/Makefile +++ b/luci-app-gately/Makefile @@ -8,20 +8,9 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-gately -PKG_VERSION:=1.0.1 -PKG_RELEASE:=1 LUCI_TITLE:=LuCI Support for Gately LUCI_DEPENDS:=+gately -LUCI_PKGARCH:=all - -define Package/$(PKG_NAME)/postinst -#!/bin/sh -[ -n "$${IPKG_INSTROOT}" ] || { - (sleep 1; /etc/init.d/rpcd reload) >/dev/null 2>&1 & -} -exit 0 -endef define Package/$(PKG_NAME)/prerm #!/bin/sh diff --git a/luci-app-gately/htdocs/luci-static/resources/view/gately/settings.js b/luci-app-gately/htdocs/luci-static/resources/view/gately/settings.js index b68f01d55..282f73a2c 100644 --- a/luci-app-gately/htdocs/luci-static/resources/view/gately/settings.js +++ b/luci-app-gately/htdocs/luci-static/resources/view/gately/settings.js @@ -41,6 +41,12 @@ return view.extend({ return Promise.all(self.pendingTasks); }).then(function() { return uci.save(); + }).then(function() { + return callSetConfig('commit_uci', ''); + }).then(function() { + if (ui.changes && typeof ui.changes.setIndicator === 'function') { + ui.changes.setIndicator(0); + } }).catch(function(err) { ui.addNotification(null, E('p', err.message || String(err)), 'error'); return Promise.reject(err); @@ -50,13 +56,14 @@ return view.extend({ handleSaveApply: function(ev, mode) { var self = this; return this.handleSave(ev).then(function() { - return ui.changes.apply(mode == '0'); - }).then(function() { var enabled = uci.get('gately', 'main', 'enabled'); var action = (enabled === '1') ? 'restart' : 'stop'; ui.showModal(null, [ E('p', { class: 'spinning' }, _('Updating configuration and restarting service...')) ]); return callService(action).then(function() { ui.hideModal(); + if (ui.changes && typeof ui.changes.setIndicator === 'function') { + ui.changes.setIndicator(0); + } ui.addNotification(null, E('p', _('Configuration updated successfully')), 'info'); uci.unload('gately'); return self.load().then(function(data) { @@ -69,32 +76,6 @@ return view.extend({ }); }, - handleUpdate: function(key, value) { - ui.showModal(null, [ E('p', { class: 'spinning' }, _('Updating configuration and restarting service...')) ]); - var self = this; - - return callSetConfig(key, value).then(function(res) { - if (res && res.ret === 0) { - var enabled = uci.get('gately', 'main', 'enabled'); - var action = (enabled === '1') ? 'restart' : 'stop'; - return callService(action).then(function() { - ui.hideModal(); - ui.addNotification(null, E('p', _('Configuration updated successfully')), 'info'); - uci.unload('gately'); - return self.load().then(function(data) { - return self.render(data); - }).then(function(node) { - var mapEl = document.querySelector('.cbi-map'); - if (mapEl && mapEl.parentNode) dom.content(mapEl.parentNode, node); - }); - }); - } else { - ui.hideModal(); - ui.addNotification(null, E('p', (res && res.error) ? res.error : _('Failed to update configuration')), 'error'); - } - }); - }, - render: function(data) { var self = this; var baseConf = (data[1] && data[1].base_conf) ? data[1].base_conf : {}; @@ -189,16 +170,6 @@ return view.extend({ self.queueSetConfig('switch_Internetaccess', false); }; - // Reset Auth - o = s.option(form.Button, '_reset_auth', _('Reset Account and Password')); - o.inputtitle = _('Reset to Default (admin / admin123456)'); - o.inputstyle = 'negative'; - o.onclick = ui.createHandlerFn(self, function() { - if (confirm(_('Are you sure you want to reset the admin account and password to default (admin / admin123456)?'))) { - return self.handleUpdate('reset_auth_info', ''); - } - }); - return m.render(); } }); diff --git a/luci-app-gately/htdocs/luci-static/resources/view/gately/status.js b/luci-app-gately/htdocs/luci-static/resources/view/gately/status.js index 98ad9b1d3..06d5ef68e 100644 --- a/luci-app-gately/htdocs/luci-static/resources/view/gately/status.js +++ b/luci-app-gately/htdocs/luci-static/resources/view/gately/status.js @@ -9,35 +9,6 @@ var callGetInfo = rpc.declare({ object: 'luci.gately', method: 'get_info', expec var callGetStatus = rpc.declare({ object: 'luci.gately', method: 'get_status', expect: { } }); var callService = rpc.declare({ object: 'luci.gately', method: 'service', params: ['action'], expect: { } }); -function pad2(n) { - return (n < 10 ? '0' : '') + n; -} - -function formatBuildDate(raw) { - if (!raw || raw === '-' || raw === 'unknown') return '-'; - var str = String(raw).trim(); - var d = null; - if (/^\d{9,13}$/.test(str)) { - var ts = parseInt(str, 10); - if (str.length <= 10) ts *= 1000; - d = new Date(ts); - } else { - var parsed = Date.parse(str); - if (!isNaN(parsed)) { - d = new Date(parsed); - } - } - if (d && !isNaN(d.getTime())) { - return d.getFullYear() + '-' + - pad2(d.getMonth() + 1) + '-' + - pad2(d.getDate()) + ' ' + - pad2(d.getHours()) + ':' + - pad2(d.getMinutes()) + ':' + - pad2(d.getSeconds()); - } - return str; -} - return view.extend({ load: function() { return callGetInfo(); @@ -118,8 +89,11 @@ return view.extend({ el = document.getElementById('_gatelyArch'); if (el) dom.content(el, info.gatelyArch || '-'); - el = document.getElementById('_gatelyDate'); - if (el) dom.content(el, formatBuildDate(gatelyInfo.Date)); + el = document.getElementById('_gatelyUser'); + if (el) dom.content(el, info.adminUser || 'admin'); + + el = document.getElementById('_gatelyPassword'); + if (el) dom.content(el, info.adminPassword || 'admin123456'); if (info.running && info.adminPort) { var url = 'http://' + window.location.hostname + ':' + info.adminPort + (info.adminSafeUrl || ''); @@ -179,8 +153,12 @@ return view.extend({ E('td', { class: 'td left', id: '_gatelyArch' }, info.gatelyArch || '-') ]), E('tr', { class: 'tr' }, [ - E('td', { class: 'td left' }, _('Compile Time')), - E('td', { class: 'td left', id: '_gatelyDate' }, formatBuildDate(gatelyInfo.Date)) + E('td', { class: 'td left' }, _('Admin Account')), + E('td', { class: 'td left', id: '_gatelyUser' }, info.adminUser || 'admin') + ]), + E('tr', { class: 'tr' }, [ + E('td', { class: 'td left' }, _('Admin Password')), + E('td', { class: 'td left', id: '_gatelyPassword' }, info.adminPassword || 'admin123456') ]), E('tr', { class: 'tr' }, [ E('td', { class: 'td left' }, _('Admin Panel')), diff --git a/luci-app-gately/po/templates/gately.pot b/luci-app-gately/po/templates/gately.pot index 549cb20ff..8721a6df3 100644 --- a/luci-app-gately/po/templates/gately.pot +++ b/luci-app-gately/po/templates/gately.pot @@ -52,7 +52,10 @@ msgstr "" msgid "Architecture" msgstr "" -msgid "Compile Time" +msgid "Admin Account" +msgstr "" + +msgid "Admin Password" msgstr "" msgid "Admin Panel" @@ -109,15 +112,6 @@ msgstr "" msgid "Allow Internet Access" msgstr "" -msgid "Reset Account and Password" -msgstr "" - -msgid "Reset to Default (admin / admin123456)" -msgstr "" - -msgid "Are you sure you want to reset the admin account and password to default (admin / admin123456)?" -msgstr "" - msgid "Updating configuration and restarting service..." msgstr "" diff --git a/luci-app-gately/po/zh_Hans/gately.po b/luci-app-gately/po/zh_Hans/gately.po index 464e58841..7d2fd3651 100644 --- a/luci-app-gately/po/zh_Hans/gately.po +++ b/luci-app-gately/po/zh_Hans/gately.po @@ -52,8 +52,11 @@ msgstr "当前版本" msgid "Architecture" msgstr "系统架构" -msgid "Compile Time" -msgstr "构建时间" +msgid "Admin Account" +msgstr "管理员账号" + +msgid "Admin Password" +msgstr "管理员密码" msgid "Admin Panel" msgstr "管理后台" @@ -109,15 +112,6 @@ msgstr "安全入口不能以 /api 或 /assets 开头,且不能包含空格、 msgid "Allow Internet Access" msgstr "允许外网访问管理后台" -msgid "Reset Account and Password" -msgstr "重置账号与密码" - -msgid "Reset to Default (admin / admin123456)" -msgstr "恢复默认认证 (admin / admin123456)" - -msgid "Are you sure you want to reset the admin account and password to default (admin / admin123456)?" -msgstr "确定要将管理员账号和密码恢复为默认值 (admin / admin123456) 吗?" - msgid "Updating configuration and restarting service..." msgstr "正在更新配置并重启服务..." diff --git a/luci-app-gately/root/usr/share/rpcd/ucode/luci.gately b/luci-app-gately/root/usr/share/rpcd/ucode/luci.gately index 13228ca71..fa56e132f 100644 --- a/luci-app-gately/root/usr/share/rpcd/ucode/luci.gately +++ b/luci-app-gately/root/usr/share/rpcd/ucode/luci.gately @@ -34,11 +34,11 @@ function is_running() { function get_version_info() { if (!access(BIN_PATH, 'x')) { - return { version: '-', commit: '-', buildDate: '-', go: '-' }; + return { version: '-', commit: '-', go: '-' }; } let p = popen(BIN_PATH + ' -version 2>/dev/null'); if (!p) { - return { version: '-', commit: '-', buildDate: '-', go: '-' }; + return { version: '-', commit: '-', go: '-' }; } let raw = trim(p.read('all') || ''); p.close(); @@ -84,6 +84,20 @@ function write_gately_json(configdir, settings) { } // Gately stores admin account in /account.json (accountName = "account") +function read_account_json(configdir) { + let path = configdir + '/account.json'; + if (!access(path, 'r')) { + return {}; + } + let content = readfile(path); + if (!content) return {}; + try { + return json(content); + } catch (e) { + return {}; + } +} + function clear_account_json(configdir) { let acc_file = configdir + '/account.json'; if (access(acc_file, 'f')) { @@ -136,6 +150,7 @@ const methods = { let ver = get_version_info(); let arch = get_arch(); let settings = read_gately_json(uci_cfg.configdir); + let account = read_account_json(uci_cfg.configdir); let effective_listen = settings.listen || ':16680'; let admin_port = parse_port_from_listen(effective_listen); @@ -144,15 +159,18 @@ const methods = { safe_path = '/' + safe_path; } let lan_only = (settings.lanOnly === true); + let admin_user = account.username || 'admin'; + let admin_password = uci_cfg.initial_password || 'admin123456'; return { running: st.running, pid: st.pid, gatelyArch: arch, + adminUser: admin_user, + adminPassword: admin_password, gatelyInfo: sprintf('%J', { Version: ver.version || '-', Commit: ver.commit || '-', - Date: ver.buildDate || '-', Go: ver.go || '-' }), adminPort: admin_port, @@ -222,12 +240,11 @@ const methods = { c.commit('gately'); clear_account_json(configdir); return { ret: 0 }; - } else if (key === 'reset_auth_info') { + } else if (key === 'commit_uci') { let c = cursor(); c.load('gately'); - c.set('gately', 'main', 'initial_password', 'admin123456'); c.commit('gately'); - clear_account_json(configdir); + system('uci -q commit gately >/dev/null 2>&1'); return { ret: 0 }; } @@ -239,6 +256,11 @@ const methods = { args: { action: 'action' }, call: function(req) { let action = req.args?.action; + let c = cursor(); + c.load('gately'); + c.commit('gately'); + system('uci -q commit gately >/dev/null 2>&1'); + if (action === 'restart' || action === 'reload') { let res = system(INIT_SCRIPT + ' stop >/dev/null 2>&1; sleep 1; ' + INIT_SCRIPT + ' start >/dev/null 2>&1'); return { result: (res === 0), ret: res }; diff --git a/luci-app-open-box/Makefile b/luci-app-open-box/Makefile index a3be06458..cc4daf026 100644 --- a/luci-app-open-box/Makefile +++ b/luci-app-open-box/Makefile @@ -1,8 +1,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-open-box -PKG_VERSION:=0.1.342 -PKG_RELEASE:=38 +PKG_VERSION:=0.1.343 +PKG_RELEASE:=39 PKG_MAINTAINER:=liandu2024 PKG_LICENSE:=GPL-3.0-or-later diff --git a/luci-app-passwall/Makefile b/luci-app-passwall/Makefile index 8c469bf48..953a71570 100644 --- a/luci-app-passwall/Makefile +++ b/luci-app-passwall/Makefile @@ -8,14 +8,13 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-passwall PKG_VERSION:=26.10.16 -PKG_RELEASE:=342 +PKG_RELEASE:=343 PKG_PO_VERSION:=$(PKG_VERSION) PKG_CONFIG_DEPENDS:= \ CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \ CONFIG_PACKAGE_$(PKG_NAME)_Nftables_Transparent_Proxy \ CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Geoview \ - CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_SmartDNS \ CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy \ CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria \ CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy \ @@ -39,7 +38,6 @@ LUCI_DEPENDS:=+coreutils +coreutils-base64 +coreutils-nohup +coreutils-timeout + +microsocks +resolveip +tcping +lyaml \ +ipt2socks \ +PACKAGE_$(PKG_NAME)_INCLUDE_Geoview:geoview \ - +PACKAGE_$(PKG_NAME)_INCLUDE_SmartDNS:smartdns \ +PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy:haproxy \ +PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria:hysteria \ +PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy:naiveproxy \ @@ -88,10 +86,7 @@ config PACKAGE_$(PKG_NAME)_Nftables_Transparent_Proxy config PACKAGE_$(PKG_NAME)_INCLUDE_Geoview bool "Include Geoview" - default y if aarch64||i386||x86_64 - -config PACKAGE_$(PKG_NAME)_INCLUDE_SmartDNS - bool "Include SmartDNS" + select PACKAGE_geoview default y if aarch64||i386||x86_64 config PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy diff --git a/luci-app-ssr-plus/Makefile b/luci-app-ssr-plus/Makefile index a0ae026aa..eba72a5b0 100644 --- a/luci-app-ssr-plus/Makefile +++ b/luci-app-ssr-plus/Makefile @@ -3,8 +3,8 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=luci-app-ssr-plus LUCI_PKGARCH:=all PKG_NAME:=luci-app-ssr-plus -PKG_VERSION:=196 -PKG_RELEASE:=93 +PKG_VERSION:=197 +PKG_RELEASE:=94 PKG_CONFIG_DEPENDS:= \ CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \ @@ -119,7 +119,7 @@ endchoice config PACKAGE_$(PKG_NAME)_INCLUDE_ChinaDNS_NG bool "Include ChinaDNS-NG" - default y if aarch64||arm||i386||x86_64 + default n config PACKAGE_$(PKG_NAME)_INCLUDE_MosDNS bool "Include MosDNS" @@ -164,7 +164,7 @@ config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_V2ray_Plugin config PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client bool "Include ShadowsocksR Libev Client" - default y + default n config PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Server bool "Include ShadowsocksR Libev Server" diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua index ce8602314..0938753a6 100644 --- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua +++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua @@ -474,11 +474,12 @@ if is_finded("xray") then o:value("wireguard", translate("WireGuard")) o:value("naive", "NaiveProxy (Xray / Cronet)") o:value("shadowsocksr", "ShadowsocksR (Xray)") + o:value("tuic", "TUIC (Xray)") end if is_finded("xray") then o:value("hysteria2", translate("Hysteria2")) end -if is_finded("mihomo") then +if is_finded("xray") or is_finded("mihomo") then o:value("snell", translate("Snell")) end o:value("socks", translate("Socks")) @@ -926,6 +927,17 @@ end o.rmempty = true o.default="auto" +o = s:option(ListValue, "xray_tuic_version", "TUIC Version") +o:value("5", "v5 (UUID / Password)") +o:value("4", "v4 (Token)") +o.default = "5" +o:depends({type = "v2ray", v2ray_protocol = "tuic"}) +o = s:option(Value, "xray_tuic_token", "TUIC Token") +o.password = true +o:depends({type = "v2ray", v2ray_protocol = "tuic", xray_tuic_version = "4"}) +o = s:option(Value, "xray_tuic_ca", translate("Certificate File Path")) +o:depends({type = "v2ray", v2ray_protocol = "tuic"}) + -- [[ TUIC ]] -- TuicNameId o = s:option(Value, "tuic_uuid", translate("TUIC User UUID")) @@ -933,6 +945,7 @@ o.password = true o.rmempty = true o.default = uuid o:depends("type", "tuic") +o:depends({type = "v2ray", v2ray_protocol = "tuic", xray_tuic_version = "5"}) --Tuic IP o = s:option(Value, "tuic_ip", translate("TUIC Server IP Address")) @@ -947,9 +960,11 @@ o.password = true o.rmempty = true o.default = "" o:depends("type", "tuic") +o:depends({type = "v2ray", v2ray_protocol = "tuic", xray_tuic_version = "5"}) o = s:option(ListValue, "udp_relay_mode", translate("UDP relay mode")) o:depends("type", "tuic") +o:depends({type = "v2ray", v2ray_protocol = "tuic"}) o:value("native", translate("native UDP characteristics")) o:value("quic", translate("lossless UDP relay using QUIC streams")) o.default = "native" @@ -957,6 +972,7 @@ o.rmempty = true o = s:option(ListValue, "congestion_control", translate("Congestion control algorithm")) o:depends("type", "tuic") +o:depends({type = "v2ray", v2ray_protocol = "tuic"}) o:value("bbr", translate("BBR")) o:value("cubic", translate("CUBIC")) o:value("new_reno", translate("New Reno")) @@ -965,12 +981,14 @@ o.rmempty = true o = s:option(Value, "heartbeat", translate("Heartbeat interval(second)")) o:depends("type", "tuic") +o:depends({type = "v2ray", v2ray_protocol = "tuic"}) o.datatype = "uinteger" o.default = "3" o.rmempty = true o = s:option(Value, "timeout", translate("Timeout for establishing a connection to server(second)")) o:depends("type", "tuic") +o:depends({type = "v2ray", v2ray_protocol = "tuic"}) o.datatype = "uinteger" o.default = "8" o.rmempty = true @@ -1006,6 +1024,7 @@ o.rmempty = true o = s:option(Flag, "zero_rtt_handshake", translate("Enable 0-RTT QUIC handshake")) o:depends("type", "tuic") +o:depends({type = "v2ray", v2ray_protocol = "tuic"}) o.default = "0" o.rmempty = true @@ -1087,10 +1106,17 @@ o = s:option(Value, "snell_obfs_host", translate("Snell Obfs Host")) o.rmempty = true o:depends({type = "v2ray", v2ray_protocol = "snell"}) +o = s:option(Flag, "snell_reuse", translate("Connection reuse")) +o.default = "0" +for _, version in ipairs({"4", "5"}) do + o:depends({type = "v2ray", v2ray_protocol = "snell", snell_version = version}) +end + -- 传输协议 o = s:option(ListValue, "transport", translate("Transport")) o:value("raw", "RAW (TCP)") o:value("kcp", "mKCP") +o:value("kcptun", "Kcptun (Xray)") o:value("ws", "WebSocket") o:value("httpupgrade", "HTTPUpgrade") o:value("xhttp", "XHTTP (SplitHTTP)") @@ -1105,6 +1131,43 @@ o:depends({type = "v2ray", v2ray_protocol = "shadowsocks"}) o:depends({type = "v2ray", v2ray_protocol = "socks"}) o:depends({type = "v2ray", v2ray_protocol = "http"}) +-- Native Kcptun transport (separate from mKCP and the external Kcptun client). +o = s:option(Value, "xray_kcptun_key", "Kcptun Key") +o:depends("transport", "kcptun") +o.password = true +o.rmempty = false +o = s:option(ListValue, "xray_kcptun_crypt", "Kcptun Encryption") +o:depends("transport", "kcptun") +for _, crypt in ipairs({"aes", "aes-128", "aes-192", "aes-128-gcm", "salsa20", "tea", "xor", "none", "null", "blowfish", "twofish", "cast5", "3des", "xtea"}) do o:value(crypt, crypt) end +o.default = "aes" +o = s:option(ListValue, "xray_kcptun_mode", "Kcptun Mode") +o:depends("transport", "kcptun") +for _, mode in ipairs({"normal", "fast", "fast2", "fast3", "manual"}) do o:value(mode, mode) end +o.default = "fast" +o = s:option(ListValue, "xray_kcptun_smuxver", "Kcptun smux Version") +o:depends("transport", "kcptun") +o:value("1", "v1") +o:value("2", "v2") +o.default = "1" +o = s:option(Flag, "xray_kcptun_nocomp", translate("Disable Compression")) +o:depends("transport", "kcptun") +o.default = "0" +o = s:option(Value, "xray_kcptun_conn", translate("Connections")) +o:depends("transport", "kcptun") +o.datatype = "range(1,256)" +o.default = "1" +o = s:option(TextValue, "xray_kcptun_options", "Kcptun Advanced Options (JSON)") +o:depends("transport", "kcptun") +o.rows = 4 +o.rmempty = true +o.description = translate("Optional MTU, FEC, windows and expiry settings. Encryption, key, compression and smux version must match the server. This transports streams over UDP; native proxy UDP is separate.") +function o.validate(self, value, section) + if not value or value:match("^%s*$") then return "" end + local ok, parsed = pcall(luci.jsonc.parse, value) + if ok and type(parsed) == "table" and value:match("^%s*{") then return value end + return nil, translate("Must be a JSON object") +end + -- [[ RAW部分 ]]-- -- TCP伪装 o = s:option(ListValue, "tcp_guise", translate("Camouflage Type")) @@ -1437,7 +1500,7 @@ if protocols and type(protocols) == "table" and #protocols > 0 then if not proto:find("^_") then if proto == "hysteria2" then o:depends({type = "v2ray", v2ray_protocol = "hysteria2", hysteria2_realms = false}) - elseif proto ~= "socks" and proto ~= "http" and proto ~= "shadowsocksr" then + elseif proto ~= "socks" and proto ~= "http" and proto ~= "shadowsocksr" and proto ~= "tuic" and proto ~= "snell" then o:depends({type = "v2ray", v2ray_protocol = proto}) end end @@ -1468,6 +1531,39 @@ o.validate = function(self, value) end end +-- ShadowTLS wraps the existing TCP proxy protocol in Xray. +o = s:option(Flag, "xray_shadowtls", "ShadowTLS (Xray)") +o.default = "0" +for _, proto in ipairs({"vmess", "vless", "trojan", "shadowsocks"}) do + o:depends({type = "v2ray", v2ray_protocol = proto, transport = "raw", tls = false, reality = false}) +end +o.description = translate("ShadowTLS protects TCP connections. The inner proxy protocol must provide encryption.") +o = s:option(ListValue, "xray_shadowtls_version", "ShadowTLS Version") +o:depends("xray_shadowtls", "1") +o:value("3", "v3") +o:value("2", "v2") +o:value("1", "v1") +o.default = "3" +o = s:option(Value, "xray_shadowtls_password", "ShadowTLS Password") +o:depends({xray_shadowtls = "1", xray_shadowtls_version = "2"}) +o:depends({xray_shadowtls = "1", xray_shadowtls_version = "3"}) +o.password = true +o.rmempty = false +o = s:option(Value, "xray_shadowtls_sni", "ShadowTLS SNI") +o:depends("xray_shadowtls", "1") +o.datatype = "hostname" +o.rmempty = false +o = s:option(ListValue, "xray_shadowtls_fingerprint", translate("TLS Client Fingerprint")) +o:depends("xray_shadowtls", "1") +for _, fp in ipairs({"chrome", "firefox", "safari", "ios", "android", "edge", "none"}) do o:value(fp, fp) end +o.default = "chrome" +o = s:option(Flag, "xray_shadowtls_strict", translate("TLS 1.3 Strict mode")) +o:depends({xray_shadowtls = "1", xray_shadowtls_version = "3"}) +o.default = "1" +o = s:option(Flag, "xray_shadowtls_insecure", translate("Allow Insecure")) +o:depends("xray_shadowtls", "1") +o.default = "0" + -- [[ TLS ]]-- o = s:option(Flag, "tls", translate("TLS")) o.rmempty = true @@ -1602,6 +1698,7 @@ o:depends("tls", true) o:depends("xtls", true) o:depends("reality", true) o.rmempty = true +o:depends({type = "v2ray", v2ray_protocol = "tuic"}) o:depends({type = "v2ray", v2ray_protocol = "naive"}) @@ -1631,6 +1728,7 @@ o:value("h3,h2,http/1.1") o:value("spdy/3.1") o:value("h3,spdy/3.1") o:depends("type", "tuic") +o:depends({type = "v2ray", v2ray_protocol = "tuic"}) -- IP STACK PREFERENCE o = s:option(ListValue, "ipstack_prefer", translate("IP Stack Preference")) @@ -1648,6 +1746,7 @@ o:depends("type", "trojan") o:depends("type", "tuic") o.description = translate("If true, allowss insecure connection at TLS client, e.g., TLS server uses unverifiable certificates.") o:depends("tls", true) +o:depends({type = "v2ray", v2ray_protocol = "tuic"}) if xray_version_val >= 260131 then -- Xray 版本大于等于 26.1.31 diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/server-config.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/server-config.lua index 0afbfc551..13950ed6d 100644 --- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/server-config.lua +++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/server-config.lua @@ -74,6 +74,10 @@ then end if nixio.fs.access("/usr/bin/xray") or nixio.fs.access("/usr/libexec/xray") then o:value("naive", "NaiveProxy (Xray)") + o:value("tuic", "TUIC (Xray)") + o:value("snell", "Snell (Xray)") + o:value("anytls", "AnyTLS (Xray)") + o:value("ssr-xray", "ShadowsocksR (Xray)") end o.default = "socks5" @@ -105,6 +109,7 @@ o:depends("type", "ss") o:depends("type", "ssr") o:depends("type", "trojan") o:depends("type", "shadowsocks") +o:depends("type", "ssr-xray") -- ========== SS/SSR 协议特有 ========== o = s:option(ListValue, "encrypt_method", translate("Encrypt Method")) @@ -235,6 +240,8 @@ o = s:option(Value, "tls_certificateFile", translate("Certificate File Path")) o.description = translate("e.g.: /etc/ssl/fullchain.pem") o.rmempty = true o:depends("tls", "1") +o:depends("type", "tuic") +o:depends("type", "anytls") o:depends("type", "naive") @@ -242,6 +249,8 @@ o = s:option(Value, "tls_keyFile", translate("Private Key File Path")) o.description = translate("e.g.: /etc/ssl/private.key") o.rmempty = true o:depends("tls", "1") +o:depends("type", "tuic") +o:depends("type", "anytls") -- ========== 传输层设置 (Transport) ========== o:depends("type", "naive") @@ -411,4 +420,61 @@ o:value("h2,h3", "HTTP/2 + HTTP/3") o.default = "h2" o:depends("type", "naive") -return m \ No newline at end of file +-- TUIC credentials are independent of the legacy server settings. +for _, field in ipairs({{"tuic_uuid", "TUIC UUID"}, {"tuic_password", "TUIC Password"}, {"tuic_token", "TUIC v4 Token"}}) do + o = s:option(Value, field[1], field[2]) + o.password = field[1] ~= "tuic_uuid" + o:depends("type", "tuic") +end + +o = s:option(Value, "snell_psk", translate("Snell PSK")) +o.password = true +o:depends("type", "snell") +o = s:option(ListValue, "snell_version", translate("Snell Version")) +for _, version in ipairs({"1", "2", "3", "4", "5"}) do o:value(version, "v" .. version) end +o.default = "4" +o:depends("type", "snell") +o = s:option(ListValue, "snell_obfs", translate("Snell Obfs")) +o:value("", translate("Disable")) +o:value("http", "HTTP") +o:value("tls", "TLS") +o:depends("type", "snell") +o = s:option(Flag, "snell_udp", translate("UDP Relay")) +o.default = "1" +for _, version in ipairs({"3", "4", "5"}) do + o:depends({type = "snell", snell_version = version}) +end + +o = s:option(Value, "anytls_password", translate("Password")) +o.password = true +o:depends("type", "anytls") +o = s:option(TextValue, "anytls_padding", "AnyTLS Padding Scheme") +o.rows = 5 +o.rmempty = true +o:depends("type", "anytls") + +-- Xray SSR has its own validated registry; keep legacy libev choices intact. +o = s:option(ListValue, "ssr_method", translate("Encrypt Method")) +for _, v in ipairs({"aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "rc4-md5", "chacha20", "chacha20-ietf", "xchacha20", "none"}) do o:value(v) end +o.default = "aes-128-cfb" +o.rmempty = false +o:depends("type", "ssr-xray") +o = s:option(ListValue, "ssr_protocol", translate("Protocol")) +for _, v in ipairs({"origin", "auth_sha1_v4", "auth_aes128_md5", "auth_aes128_sha1", "auth_chain_a", "auth_chain_b"}) do o:value(v) end +o.default = "auth_aes128_sha1" +o.rmempty = false +o:depends("type", "ssr-xray") +o = s:option(Value, "ssr_protocol_param", "SSR Protocol Param") +o.description = "1001:password1,1002:password2" +o:depends("type", "ssr-xray") +o = s:option(ListValue, "ssr_obfs", translate("Obfs")) +for _, v in ipairs({"plain", "http_simple", "http_post", "random_head", "tls1.2_ticket_auth", "tls1.2_ticket_fastauth"}) do o:value(v) end +o.default = "plain" +o.rmempty = false +o:depends("type", "ssr-xray") +o = s:option(Value, "ssr_obfs_param", translate("Obfs param (optional)")) +o.description = "HTTP Host: example.com,example.org" +o:depends({type = "ssr-xray", ssr_obfs = "http_simple"}) +o:depends({type = "ssr-xray", ssr_obfs = "http_post"}) + +return m diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/server.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/server.lua index c2ac03c60..55390a650 100644 --- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/server.lua +++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/server.lua @@ -111,6 +111,7 @@ function o.cfgvalue(self, section) elseif val == "vless" then return "VLESS (Xray)" elseif val == "trojan" then return "Trojan (Xray)" elseif val == "shadowsocks" then return "Shadowsocks (Xray)" + elseif val == "ssr-xray" then return "ShadowsocksR (Xray)" else return val end end @@ -126,6 +127,7 @@ end o = sec:option(DummyValue, "encrypt_method", translate("Encrypt Method")) function o.cfgvalue(self, section) + if self.map:get(section, "type") == "ssr-xray" then return (self.map:get(section, "ssr_method") or "-"):upper() end local method = self.map:get(section, "encrypt_method") or self.map:get(section, "encrypt_method_ss") return method and method:upper() or "-" end diff --git a/luci-app-ssr-plus/root/etc/init.d/shadowsocksr b/luci-app-ssr-plus/root/etc/init.d/shadowsocksr index 36cb409d6..bc43b4ce6 100755 --- a/luci-app-ssr-plus/root/etc/init.d/shadowsocksr +++ b/luci-app-ssr-plus/root/etc/init.d/shadowsocksr @@ -992,8 +992,10 @@ use_mihomo_for_ss_rust_server() { } start_v2ray_with_mihomo() { + [ "$(uci_get_by_name "$1" transport)" = "kcptun" ] && return 1 + [ "$(uci_get_by_name "$1" xray_shadowtls 0)" = "1" ] && return 1 # These nodes use Xray-specific settings. - case "$(uci_get_by_name "$1" v2ray_protocol)" in naive|shadowsocksr) return 1 ;; esac + case "$(uci_get_by_name "$1" v2ray_protocol)" in naive|shadowsocksr|tuic) return 1 ;; esac local sid="$1" local local_port="$2" local socks_port="$3" @@ -1798,7 +1800,7 @@ start_udp() { ;; v2ray) enable_mihomo=$(uci_get_by_type server_subscribe enable_mihomo "") - if [ "$(uci_get_by_name "$UDP_RELAY_SERVER" v2ray_protocol)" != "naive" ] && [ "$(uci_get_by_name "$UDP_RELAY_SERVER" v2ray_protocol)" != "shadowsocksr" ] && [ "$enable_mihomo" = "1" ] && has_mihomo_binary; then + if [ "$(uci_get_by_name "$UDP_RELAY_SERVER" v2ray_protocol)" != "naive" ] && [ "$(uci_get_by_name "$UDP_RELAY_SERVER" v2ray_protocol)" != "shadowsocksr" ] && [ "$(uci_get_by_name "$UDP_RELAY_SERVER" v2ray_protocol)" != "tuic" ] && [ "$enable_mihomo" = "1" ] && has_mihomo_binary; then redir_udp=0 ARG_UDP="" echolog "UDP TPROXY Relay:V2Ray/Xray 单节点由主 Mihomo 实例处理。" @@ -1811,7 +1813,7 @@ start_udp() { echolog "UDP TPROXY Relay:$($(first_type xray) version | head -1) Started!" else # Xray 不存在但 Mihomo 存在,回退到 Mihomo(当 enable_mihomo=0 时) - if [ "$(uci_get_by_name "$UDP_RELAY_SERVER" v2ray_protocol)" != "naive" ] && [ "$(uci_get_by_name "$UDP_RELAY_SERVER" v2ray_protocol)" != "shadowsocksr" ] && has_mihomo_binary; then + if [ "$(uci_get_by_name "$UDP_RELAY_SERVER" v2ray_protocol)" != "naive" ] && [ "$(uci_get_by_name "$UDP_RELAY_SERVER" v2ray_protocol)" != "shadowsocksr" ] && [ "$(uci_get_by_name "$UDP_RELAY_SERVER" v2ray_protocol)" != "tuic" ] && has_mihomo_binary; then redir_udp=0 ARG_UDP="" echolog "UDP TPROXY Relay:Xray 内核不存在,已回退到 Mihomo 内核启动。" @@ -1962,9 +1964,6 @@ start_local() { [ -n "$probe_instance_key" ] && instance_key="$probe_instance_key" if [ "$enable_mihomo" = "1" ] && start_v2ray_with_mihomo "$LOCAL_SERVER" "$local_port" "$local_port" "$instance_key" "socks" "v2ray-local" "Global_Socks5"; then : - elif [ "$(uci_get_by_name "$LOCAL_SERVER" v2ray_protocol)" = "snell" ]; then - echolog "Global_Socks5:Snell 需要 Mihomo 内核,且当前 Mihomo 启动失败。" - return 1 elif [ "$_local" = "2" ]; then # 尝试 Xray xray_bin="$(first_type xray)" @@ -2250,7 +2249,14 @@ get_udp_relay_mode() { ;; v2ray) proto=$(uci_get_by_name "$node" v2ray_protocol) + if [ "$(uci_get_by_name "$node" transport)" = "kcptun" ]; then + case "$proto" in vmess|vless|trojan) echo "native" ;; *) echo "disabled" ;; esac + return + fi case "$proto" in + snell) + case "$(uci_get_by_name "$node" snell_version 4)" in 1|2) echo "disabled" ;; *) echo "native" ;; esac + ;; naive) [ "$(uci_get_by_name "$node" naive_udp_over_tcp 0)" = "1" ] && echo "native" || echo "disabled" ;; @@ -2386,11 +2392,8 @@ Start_Run() { enable_mihomo=$(uci_get_by_type server_subscribe enable_mihomo "") if [ "$enable_mihomo" = "1" ] && start_v2ray_with_mihomo "$GLOBAL_SERVER" "$tcp_port" "$socks_port" "$GLOBAL_SERVER" "redir" "ssr-retcp" "Main node"; then : - elif [ "$(uci_get_by_name "$GLOBAL_SERVER" v2ray_protocol)" = "snell" ]; then - echolog "Main node:Snell 需要 Mihomo 内核,且当前 Mihomo 启动失败。" - return 1 else - # enable_mihomo=0 或 Mihomo 启动失败(非 Snell)→ 优先使用 Xray + # enable_mihomo=0 或 Mihomo 启动失败 → 优先使用 Xray xray_bin="$(first_type xray)" if [ -n "$xray_bin" ]; then gen_config_file "$GLOBAL_SERVER" "$type" 1 "$tcp_port" "$socks_port" @@ -2752,6 +2755,46 @@ start_server() { echolog "Server: Xray NaiveProxy Server$server_count Started!" ;; + ssr-xray) + xray_bin="$(first_type xray)" + [ -x "$xray_bin" ] || return 1 + config_file="$TMP_PATH/xray-server-$server_count.json" + lua /usr/share/shadowsocksr/gen_ssr_server.lua "$1" > "$config_file" || return 1 + "$xray_bin" run -test -c "$config_file" >> /var/ssrplus.log 2>&1 || return 1 + ln_start_bin "$xray_bin" xray-server-$server_count run -c "$config_file" + echolog "Server: Xray ShadowsocksR Server$server_count Started!" + ;; + + anytls) + xray_bin="$(first_type xray)" + [ -x "$xray_bin" ] || return 1 + config_file="$TMP_PATH/xray-server-$server_count.json" + lua /usr/share/shadowsocksr/gen_anytls_server.lua "$1" > "$config_file" || return 1 + "$xray_bin" run -test -c "$config_file" >> /var/ssrplus.log 2>&1 || return 1 + ln_start_bin "$xray_bin" xray-server-$server_count run -c "$config_file" + echolog "Server: Xray AnyTLS Server$server_count Started!" + ;; + + snell) + xray_bin="$(first_type xray)" + [ -x "$xray_bin" ] || return 1 + config_file="$TMP_PATH/xray-server-$server_count.json" + lua /usr/share/shadowsocksr/gen_snell_server.lua "$1" > "$config_file" || return 1 + "$xray_bin" run -test -c "$config_file" >> /var/ssrplus.log 2>&1 || return 1 + ln_start_bin "$xray_bin" xray-server-$server_count run -c "$config_file" + echolog "Server: Xray Snell Server$server_count Started!" + ;; + + tuic) + xray_bin="$(first_type xray)" + [ -x "$xray_bin" ] || return 1 + config_file="$TMP_PATH/xray-server-$server_count.json" + lua /usr/share/shadowsocksr/gen_tuic_server.lua "$1" > "$config_file" || return 1 + "$xray_bin" run -test -c "$config_file" >> /var/ssrplus.log 2>&1 || return 1 + ln_start_bin "$xray_bin" xray-server-$server_count run -c "$config_file" + echolog "Server: Xray TUIC Server$server_count Started!" + ;; + vmess|vless|trojan|shadowsocks) # Xray 服务端支持 port=$(uci_get_by_name "$1" server_port) diff --git a/luci-app-ssr-plus/root/usr/share/shadowsocksr/clash_yaml.lua b/luci-app-ssr-plus/root/usr/share/shadowsocksr/clash_yaml.lua index beda6d74c..2099ede42 100644 --- a/luci-app-ssr-plus/root/usr/share/shadowsocksr/clash_yaml.lua +++ b/luci-app-ssr-plus/root/usr/share/shadowsocksr/clash_yaml.lua @@ -928,7 +928,9 @@ local function build_v2ray_mihomo_proxy(sid) elseif protocol == "snell" then proxy.type = "snell" proxy.psk = get_server_field(sid, "snell_psk", "") - proxy.version = number_or_nil(get_server_field(sid, "snell_version", "")) + proxy.version = tonumber(get_server_field(sid, "snell_version", "4")) or 4 + proxy.reuse = get_server_field(sid, "snell_reuse", "0") == "1" + proxy.udp = proxy.version >= 3 local obfs_mode = string_or_nil(get_server_field(sid, "snell_obfs", "")) local obfs_host = string_or_nil(get_server_field(sid, "snell_obfs_host", "")) if obfs_mode or obfs_host then diff --git a/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_anytls_server.lua b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_anytls_server.lua new file mode 100644 index 000000000..9da320999 --- /dev/null +++ b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_anytls_server.lua @@ -0,0 +1,17 @@ +#!/usr/bin/lua +local uci = require("luci.model.uci").cursor() +local json = require "luci.jsonc" +local s = assert(uci:get_all("shadowsocksr", arg[1]), "missing server section") +local padding = {} +for line in (s.anytls_padding or ""):gmatch("[^\r\n]+") do padding[#padding + 1] = line end +print(json.stringify({ + log = {loglevel = "warning"}, + inbounds = {{listen = "::", port = assert(tonumber(s.server_port)), protocol = "anytls", + settings = {users = {{email = "user", password = assert(s.anytls_password)}}, + paddingScheme = #padding > 0 and padding or nil}, + streamSettings = {network = "raw", security = "tls", tlsSettings = { + certificates = {{oneTimeLoading = true, certificateFile = assert(s.tls_certificateFile), keyFile = assert(s.tls_keyFile)}} + }} + }}, + outbounds = {{protocol = "freedom", settings = {}}} +}, 1)) diff --git a/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua index 710cb8242..4f0eabf58 100755 --- a/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua +++ b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua @@ -363,6 +363,34 @@ function outbound:handleIndex(index) trojan = function() trojan_shadowsocks() end, + snell = function() + outbound_settings = { + address = server.server, port = tonumber(server.server_port), + psk = server.snell_psk, version = tonumber(server.snell_version) or 4, + obfs = server.snell_obfs or "", obfsHost = server.snell_obfs_host, + reuse = server.snell_reuse == "1" + } + end, + tuic = function() + local version = tonumber(server.xray_tuic_version) or 5 + local alpn = {} + for value in (server.tuic_alpn or ""):gmatch("[^,]+") do alpn[#alpn + 1] = value end + if #alpn == 0 then alpn = {"h3"} end + outbound_settings = { + address = server.server, port = tonumber(server.server_port), version = version, + token = version == 4 and server.xray_tuic_token or nil, + uuid = version == 5 and server.tuic_uuid or nil, + password = version == 5 and server.tuic_passwd or nil, + serverName = server.tls_host, certificateFile = server.xray_tuic_ca, + allowInsecure = server.insecure == "1", + alpn = alpn, + congestionControl = server.congestion_control or "cubic", + udpRelayMode = server.udp_relay_mode or "native", + heartbeatInterval = (tonumber(server.heartbeat) or 10) * 1000, + requestTimeout = (tonumber(server.timeout) or 8) * 1000, + reduceRtt = server.zero_rtt_handshake == "1" + } + end, shadowsocksr = function() outbound_settings = { address = server.server, port = tonumber(server.server_port), @@ -899,14 +927,53 @@ Xray.outbounds = { } } --- Naive owns TLS/multiplexing; SSR owns obfuscation and uses native UDP. --- Preserve Xray socket marks and dialer chains for both. -if server.v2ray_protocol == "naive" or server.v2ray_protocol == "shadowsocksr" then +-- These protocols own their framing, obfuscation and multiplexing. +-- Preserve Xray socket marks and dialer chains. +if server.v2ray_protocol == "naive" or server.v2ray_protocol == "shadowsocksr" or server.v2ray_protocol == "tuic" or server.v2ray_protocol == "snell" then local ob = Xray.outbounds[1] ob.streamSettings = { network = "raw", sockopt = ob.streamSettings.sockopt } ob.mux = nil end +if server.type == "v2ray" and server.transport == "kcptun" then + local supported = {vmess = true, vless = true, trojan = true, shadowsocks = true, socks = true, http = true} + assert(supported[server.v2ray_protocol], "Kcptun requires a supported stream proxy protocol") + assert(server.tls ~= "1" and server.reality ~= "1" and server.xtls ~= "1", "Kcptun cannot be combined with stream TLS or REALITY") + assert(server.kcp_enable ~= "1", "Native Kcptun cannot be combined with the external Kcptun client") + assert(not server.tls_flow or server.tls_flow == "", "Kcptun cannot be combined with XTLS flow") + local options = {} + if server.xray_kcptun_options and server.xray_kcptun_options:match("%S") then + assert(server.xray_kcptun_options:match("^%s*{"), "Kcptun options must be a JSON object") + options = json.parse(server.xray_kcptun_options) + assert(type(options) == "table", "Kcptun options must be a JSON object") + end + options.key = server.xray_kcptun_key + options.crypt = server.xray_kcptun_crypt or "aes" + options.mode = server.xray_kcptun_mode or "fast" + options.smuxver = tonumber(server.xray_kcptun_smuxver) or 1 + options.nocomp = server.xray_kcptun_nocomp == "1" + options.conn = tonumber(server.xray_kcptun_conn) or 1 + Xray.outbounds[1].streamSettings.kcptunSettings = options +end + +if server.type == "v2ray" and server.xray_shadowtls == "1" then + local supported = {vmess = true, vless = true, trojan = true, shadowsocks = true} + assert(supported[server.v2ray_protocol], "ShadowTLS requires a supported TCP proxy protocol") + assert((server.transport or "raw") == "raw" or server.transport == "tcp", "ShadowTLS requires RAW/TCP") + assert(server.tls ~= "1" and server.reality ~= "1", "ShadowTLS cannot be combined with TLS or REALITY") + assert(not server.tls_flow or server.tls_flow == "", "ShadowTLS cannot be combined with XTLS flow") + local stream = Xray.outbounds[1].streamSettings + stream.security = "shadowtls" + stream.shadowtlsSettings = { + version = tonumber(server.xray_shadowtls_version) or 3, + password = server.xray_shadowtls_password, + serverName = server.xray_shadowtls_sni, + fingerprint = server.xray_shadowtls_fingerprint or "chrome", + strictMode = server.xray_shadowtls_strict ~= "0", + allowInsecure = server.xray_shadowtls_insecure == "1" + } +end + if xray_builtin_dns then table.insert(Xray.outbounds, { protocol = "dns", @@ -1322,6 +1389,14 @@ function config:handleIndex(index) chain_switch[ChainType]() end end, + snell = function() + outbound_settings = { + address = server.server, port = tonumber(server.server_port), + psk = server.snell_psk, version = tonumber(server.snell_version) or 4, + obfs = server.snell_obfs or "", obfsHost = server.snell_obfs_host, + reuse = server.snell_reuse == "1" + } + end, tuic = function() print(json.stringify(tuic, 1)) end diff --git a/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_snell_server.lua b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_snell_server.lua new file mode 100644 index 000000000..832fc3a21 --- /dev/null +++ b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_snell_server.lua @@ -0,0 +1,13 @@ +#!/usr/bin/lua +local uci = require("luci.model.uci").cursor() +local json = require "luci.jsonc" +local s = assert(uci:get_all("shadowsocksr", arg[1]), "missing server section") +local version = tonumber(s.snell_version) or 4 +print(json.stringify({ + log = {loglevel = "warning"}, + inbounds = {{listen = "::", port = assert(tonumber(s.server_port)), protocol = "snell", + settings = {psk = assert(s.snell_psk), version = version, + obfs = s.snell_obfs or "", udp = version >= 3 and s.snell_udp ~= "0"} + }}, + outbounds = {{protocol = "freedom", settings = {}}} +}, 1)) diff --git a/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_ssr_server.lua b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_ssr_server.lua new file mode 100644 index 000000000..bb22aaa40 --- /dev/null +++ b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_ssr_server.lua @@ -0,0 +1,13 @@ +#!/usr/bin/lua +local uci = require("luci.model.uci").cursor() +local json = require "luci.jsonc" +local s = assert(uci:get_all("shadowsocksr", arg[1]), "missing server section") +print(json.stringify({ + log = {loglevel = "warning"}, + inbounds = {{listen = "::", port = assert(tonumber(s.server_port)), protocol = "shadowsocksr", + settings = {password = assert(s.password), method = s.ssr_method or "aes-128-cfb", + protocol = s.ssr_protocol or "auth_aes128_sha1", protocolParam = s.ssr_protocol_param or "", + obfs = s.ssr_obfs or "plain", obfsParam = s.ssr_obfs_param or "", network = "tcp,udp"} + }}, + outbounds = {{protocol = "freedom", settings = {}}} +}, 1)) diff --git a/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_tuic_server.lua b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_tuic_server.lua new file mode 100644 index 000000000..f0ab48f2f --- /dev/null +++ b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_tuic_server.lua @@ -0,0 +1,19 @@ +#!/usr/bin/lua +local uci = require("luci.model.uci").cursor() +local json = require "luci.jsonc" +local s = assert(uci:get_all("shadowsocksr", arg[1]), "missing server section") +local users, tokens = {}, {} +if s.tuic_uuid and s.tuic_uuid ~= "" then + users[s.tuic_uuid] = assert(s.tuic_password, "TUIC password required") +end +if s.tuic_token and s.tuic_token ~= "" then tokens[1] = s.tuic_token end +assert(next(users) or #tokens > 0, "TUIC credentials required") +print(json.stringify({ + log = {loglevel = "warning"}, + inbounds = {{listen = "::", port = assert(tonumber(s.server_port)), protocol = "tuic", + settings = {users = next(users) and users or nil, tokens = #tokens > 0 and tokens or nil, + certificateFile = assert(s.tls_certificateFile), keyFile = assert(s.tls_keyFile)}, + streamSettings = {network = "tuic"} + }}, + outbounds = {{protocol = "freedom", settings = {}}} +}, 1)) diff --git a/luci-app-ssr-plus/root/usr/share/shadowsocksr/subscribe.lua b/luci-app-ssr-plus/root/usr/share/shadowsocksr/subscribe.lua index 589f5093c..05307242b 100755 --- a/luci-app-ssr-plus/root/usr/share/shadowsocksr/subscribe.lua +++ b/luci-app-ssr-plus/root/usr/share/shadowsocksr/subscribe.lua @@ -824,7 +824,9 @@ local function to_mihomo_proxy(node) elseif node.type == "v2ray" and node.v2ray_protocol == "snell" then proxy.type = "snell" proxy.psk = node.snell_psk - proxy.version = tonumber(node.snell_version) or nil + proxy.version = tonumber(node.snell_version) or 4 + proxy.udp = proxy.version >= 3 + proxy.reuse = node.snell_reuse == "1" local obfs_mode = string_from_value(node.snell_obfs) local obfs_host = string_from_value(node.snell_obfs_host) if obfs_mode or obfs_host then @@ -1021,8 +1023,8 @@ local function processData(szType, content, cfgid) result.fast_open = params.tfo end elseif szType == "snell" then - if not has_mihomo then - log("跳过 Snell 节点:本地未安装 mihomo。") + if not has_xray and not has_mihomo then + log("跳过 Snell 节点:本地未安装 Xray 或 Mihomo。") return nil end @@ -1041,6 +1043,7 @@ local function processData(szType, content, cfgid) result.snell_version = first_nonempty(params, {"version", "v"}) or "4" result.snell_obfs = first_nonempty(params, {"obfs", "obfs-mode", "obfs_mode"}) result.snell_obfs_host = first_nonempty(params, {"obfs-host", "obfs_host", "host"}) + result.snell_reuse = (params.reuse == "1" or params.reuse == "true") and "1" or "0" if not result.snell_psk or result.snell_psk == "" then result.server = nil end diff --git a/luci-app-trafficctl/Makefile b/luci-app-trafficctl/Makefile index 7773a3def..5c8caaa2f 100644 --- a/luci-app-trafficctl/Makefile +++ b/luci-app-trafficctl/Makefile @@ -1,8 +1,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-trafficctl -PKG_VERSION:=1.23.0 -PKG_RELEASE:=13 +PKG_VERSION:=1.23.1 +PKG_RELEASE:=14 PKG_LICENSE:=Apache-2.0 PKG_MAINTAINER:=Denis Iusupov diff --git a/luci-app-trafficctl/po/templates/luci-app-trafficctl.pot b/luci-app-trafficctl/po/templates/luci-app-trafficctl.pot index ca1baa04c..b2e11721b 100644 --- a/luci-app-trafficctl/po/templates/luci-app-trafficctl.pot +++ b/luci-app-trafficctl/po/templates/luci-app-trafficctl.pot @@ -13,6 +13,12 @@ msgstr "" msgid "%d pkts, %s dropped" msgstr "" +msgid "%s down / %s up" +msgstr "" + +msgid "%s down and %s up" +msgstr "" + msgid "%s total" msgstr "" @@ -46,9 +52,7 @@ msgstr "" msgid "50 Mbit/s" msgstr "" -msgid "" -"Accelerates routing in the kernel via nftables flowtable. Speed monitoring and t" -"raffic shaping work normally." +msgid "Accelerates routing in the kernel via nftables flowtable. Speed monitoring and traffic shaping work normally." msgstr "" msgid "Accumulated on the router" @@ -81,6 +85,9 @@ msgstr "" msgid "Adding to" msgstr "" +msgid "Aggregate every stored period" +msgstr "" + msgid "All" msgstr "" @@ -96,9 +103,7 @@ msgstr "" msgid "App" msgstr "" -msgid "" -"Applied once, the first time a device appears on the network. A device that alre" -"ady has a limit is left alone." +msgid "Applied once, the first time a device appears on the network. A device that already has a limit is left alone." msgstr "" msgid "Applied — firewall reloading" @@ -152,9 +157,7 @@ msgstr "" msgid "Bridge port — these bytes are also counted in its bridge" msgstr "" -msgid "" -"Bridge ports, switch conduits and ifb mirrors. Their bytes are already counted i" -"n the bridge or uplink above." +msgid "Bridge ports, switch conduits and ifb mirrors. Their bytes are already counted in the bridge or uplink above." msgstr "" msgid "Byte totals unavailable on this router." @@ -283,17 +286,10 @@ msgstr "" msgid "Devices already known:" msgstr "" -msgid "" -"Devices keep talking to each other and to the router — only the way out is close" -"d. Traffic is stopped on the way in, so a transparent proxy on the router (podko" -"p, passwall) cannot carry it out either. Remote access that reaches the router t" -"hrough a LAN host (Tailscale, a tunnel, a jump host) stops working while this is" -" on." +msgid "Devices keep talking to each other and to the router — only the way out is closed. Traffic is stopped on the way in, so a transparent proxy on the router (podkop, passwall) cannot carry it out either. Remote access that reaches the router through a LAN host (Tailscale, a tunnel, a jump host) stops working while this is on." msgstr "" -msgid "" -"Devices on the WiFi deny list that are still connected — those blocks are not in" -" effect on the running radio." +msgid "Devices on the WiFi deny list that are still connected — those blocks are not in effect on the running radio." msgstr "" msgid "Disable hardware offload (keeps software offload):" @@ -311,6 +307,9 @@ msgstr "" msgid "Done" msgstr "" +msgid "Download" +msgstr "" + msgid "Download · %s" msgstr "" @@ -353,6 +352,12 @@ msgstr "" msgid "Every device this router forwards for" msgstr "" +msgid "Exact. Byte counts come from cumulative nftables counters, so they do not depend on how often the sampler runs. The TCP/UDP split is unavailable from this source." +msgstr "" + +msgid "Export CSV" +msgstr "" + msgid "Ext. port" msgstr "" @@ -374,11 +379,10 @@ msgstr "" msgid "Flow Offload" msgstr "" -msgid "" -"Flow offload is active and the kernel does not support the nftables counter maps" -" trafficctl uses in that mode, so byte counters stop updating for offloaded conn" -"ections. Speeds and totals would be far too low, so they are not shown. Disablin" -"g hardware offload in Settings → Flow Offload restores them." +msgid "Flow offload is active and the kernel does not support the nftables counter maps trafficctl uses in that mode, so byte counters stop updating for offloaded connections. Speeds and totals would be far too low, so they are not shown. Disabling hardware offload in Settings → Flow Offload restores them." +msgstr "" + +msgid "Flow offloading is active." msgstr "" msgid "Flow: /devices → select device → inline keyboard with enabled actions above" @@ -387,9 +391,16 @@ msgstr "" msgid "Flows" msgstr "" -msgid "" -"Forwarded traffic only — this kernel cannot install the prerouting rule, so traf" -"fic handled by a transparent proxy on the router would not be caught." +msgid "Flush failed" +msgstr "" + +msgid "Flush now" +msgstr "" + +msgid "Flushing…" +msgstr "" + +msgid "Forwarded traffic only — this kernel cannot install the prerouting rule, so traffic handled by a transparent proxy on the router would not be caught." msgstr "" msgid "Full control" @@ -398,6 +409,9 @@ msgstr "" msgid "Gbit/s" msgstr "" +msgid "Give upload its own ceiling instead of matching download" +msgstr "" + msgid "Global internet cut needs nftables (fw4). This router is running iptables." msgstr "" @@ -428,15 +442,16 @@ msgstr "" msgid "Hardware offload" msgstr "" +msgid "History is not recorded by default: it writes to the router's flash on a schedule, and every other persistent store in this app was deliberately kept off that path. Enable it below." +msgstr "" + msgid "Hostname" msgstr "" msgid "Hostname / Dst IP" msgstr "" -msgid "" -"How often live data is polled: speeds, graphs, drop/backlog counters, and the co" -"nnection table in device view" +msgid "How often live data is polled: speeds, graphs, drop/backlog counters, and the connection table in device view" msgstr "" msgid "How to get full functionality without disabling offload:" @@ -445,10 +460,10 @@ msgstr "" msgid "IP address" msgstr "" -msgid "" -"If your current firmware uses fw3 (iptables) or ships a kernel older than 5.7, a" -" router with modern OpenWrt and fw4 support will have this working out of the bo" -"x." +msgid "IP-keyed" +msgstr "" + +msgid "If your current firmware uses fw3 (iptables) or ships a kernel older than 5.7, a router with modern OpenWrt and fw4 support will have this working out of the box." msgstr "" msgid "Iface" @@ -457,18 +472,13 @@ msgstr "" msgid "In" msgstr "" -msgid "" -"In this mode the kernel's conntrack, firewall, and tc are bypassed for offloaded" -" flows:" +msgid "In this mode the kernel's conntrack, firewall, and tc are bypassed for offloaded flows:" msgstr "" msgid "Inbound rate limit (drops packets above the rate)" msgstr "" -msgid "" -"Inbound traffic control for DNAT port forwards and router-local open ports confi" -"gured in the firewall. Pause drops all inbound traffic on the port instantly (wi" -"thout touching the firewall config); Limit polices the inbound rate." +msgid "Inbound traffic control for DNAT port forwards and router-local open ports configured in the firewall. Pause drops all inbound traffic on the port instantly (without touching the firewall config); Limit polices the inbound rate." msgstr "" msgid "Inet" @@ -501,6 +511,9 @@ msgstr "" msgid "LAN keeps working" msgstr "" +msgid "Last seen" +msgstr "" + msgid "Last seen: %s, %s ago" msgstr "" @@ -534,10 +547,7 @@ msgstr "" msgid "Limiter (nft drop)" msgstr "" -msgid "Limiting %s → %s (%s)…" -msgstr "" - -msgid "Limiting → %s…" +msgid "Limiting" msgstr "" msgid "Link" @@ -561,6 +571,9 @@ msgstr "" msgid "Logging & Persistence" msgstr "" +msgid "Lower bound, not a measurement. Flow offloading is active without counter support, so the kernel counters this history is built from stop moving while traffic keeps flowing. The real figures are higher by an unknown amount — use these only as a floor." +msgstr "" + msgid "MAC address" msgstr "" @@ -585,6 +598,9 @@ msgstr "" msgid "New devices" msgstr "" +msgid "No MAC address is known for this device, so its history is keyed on its IP address. If that address is reassigned, earlier traffic recorded here may belong to a different device." +msgstr "" + msgid "No action buttons enabled" msgstr "" @@ -597,9 +613,7 @@ msgstr "" msgid "No activity recorded yet." msgstr "" -msgid "" -"No baseline recorded yet. It is taken from the current leases when you switch th" -"is on." +msgid "No baseline recorded yet. It is taken from the current leases when you switch this on." msgstr "" msgid "No devices match filter." @@ -617,23 +631,19 @@ msgstr "" msgid "No port forwards or WAN-open ports are configured in the firewall." msgstr "" +msgid "No traffic has been recorded for this period yet. The first figures appear after the next flush." +msgstr "" + msgid "None (per-flow)" msgstr "" -msgid "" -"Not counted: flow offload is active and this router cannot provide the nftables " -"counters trafficctl uses in that mode, so the kernel byte counters stop updating" -" for offloaded connections. Any total here would be far too low. See Settings → " -"Flow Offload." +msgid "Not counted: flow offload is active and this router cannot provide the nftables counters trafficctl uses in that mode, so the kernel byte counters stop updating for offloaded connections. Any total here would be far too low. See Settings → Flow Offload." msgstr "" msgid "Not enough data yet" msgstr "" -msgid "" -"Not measurable while flow offload is active: in that mode the router counts byte" -"s per address only, so traffic cannot be split by protocol. See Settings → Flow " -"Offload." +msgid "Not measurable while flow offload is active: in that mode the router counts bytes per address only, so traffic cannot be split by protocol. See Settings → Flow Offload." msgstr "" msgid "Note: speed is measured before shaper — bursts above limit are normal" @@ -648,19 +658,16 @@ msgstr "" msgid "Off" msgstr "" -msgid "" -"Offloads routing to the hardware engine (PPE/NPU). Requires software offload. ⚠ " -"On many platforms (e.g. Mediatek Filogic) the driver does not report byte counte" -"rs back to the kernel — real-time speed monitoring will show zero." +msgid "Off." +msgstr "" + +msgid "Offloads routing to the hardware engine (PPE/NPU). Requires software offload. ⚠ On many platforms (e.g. Mediatek Filogic) the driver does not report byte counters back to the kernel — real-time speed monitoring will show zero." msgstr "" msgid "On %s" msgstr "" -msgid "" -"On the WiFi deny list but still connected — the block is NOT in effect on the ru" -"nning radio. Install hostapd-utils so blocks apply immediately, or restart WiFi " -"(disconnects every client)." +msgid "On the WiFi deny list but still connected — the block is NOT in effect on the running radio. Install hostapd-utils so blocks apply immediately, or restart WiFi (disconnects every client)." msgstr "" msgid "One bucket for the whole subnet" @@ -711,6 +718,15 @@ msgstr "" msgid "Pause" msgstr "" +msgid "Per-device totals accumulated per accounting period, including devices that are not connected now. Figures survive reboots; the live view on the Devices tab does not." +msgstr "" + +msgid "Period" +msgstr "" + +msgid "Periods" +msgstr "" + msgid "Persist rules" msgstr "" @@ -738,13 +754,16 @@ msgstr "" msgid "Protocol" msgstr "" +msgid "Qualcomm NSS acceleration is active" +msgstr "" + msgid "Queue" msgstr "" msgid "Queue memory" msgstr "" -msgid "Queued: %s" +msgid "Queued: %s" msgstr "" msgid "Queues excess traffic (smoother streaming, lower jitter)" @@ -756,6 +775,9 @@ msgstr "" msgid "Rate limiting — applies only to new connections" msgstr "" +msgid "Rate limits and traffic shaping cannot be enforced while it is: accelerated flows bypass the Linux network stack, so rules are installed but have no effect. The toggles below do not control this — stop the ECM front end (front_end_ipv4_stop / front_end_ipv6_stop under /sys/kernel/debug/ecm) or set acceleration_engine in /etc/config/ecm, then re-apply the limit." +msgstr "" + msgid "Ratelimits" msgstr "" @@ -765,6 +787,9 @@ msgstr "" msgid "Recent" msgstr "" +msgid "Recording is paused: the last write failed." +msgstr "" + msgid "Refresh" msgstr "" @@ -810,6 +835,9 @@ msgstr "" msgid "Running…" msgstr "" +msgid "Sampled. Byte counts come from conntrack, which only reports flows that are alive when it is read — a connection that both starts and finishes between two samples is not counted. Shorten the sampling interval to narrow that gap." +msgstr "" + msgid "Save" msgstr "" @@ -849,7 +877,7 @@ msgstr "" msgid "Shaped:" msgstr "" -msgid "Shaped: 🌊 %s" +msgid "Shaped: 🌊 %s" msgstr "" msgid "Shaper" @@ -861,12 +889,15 @@ msgstr "" msgid "Shapes" msgstr "" -msgid "Shaping → %s…" +msgid "Shaping" msgstr "" msgid "Share:" msgstr "" +msgid "Show only this period" +msgstr "" + msgid "Show/hide token" msgstr "" @@ -879,6 +910,9 @@ msgstr "" msgid "Software flow offload" msgstr "" +msgid "Software flow offloading is active, with counter sync." +msgstr "" + msgid "Software flow offloading is active." msgstr "" @@ -888,18 +922,19 @@ msgstr "" msgid "Software offload (counter)" msgstr "" +msgid "Sort by this column" +msgstr "" + msgid "Speed Limit" msgstr "" msgid "Speed graph. Window = avg time. Orange dashed line = speed limit" msgstr "" -msgid "" -"Speed is measured via nftables counters installed at a higher priority (before t" -"he flowtable), so graphs are accurate." +msgid "Speed is measured via nftables counters installed at a higher priority (before the flowtable), so graphs are accurate." msgstr "" -msgid "Speed limit: ⚡ %s" +msgid "Speed limit: ⚡ %s" msgstr "" msgid "Speed limit: shaper (queue) or limiter (drop)" @@ -908,6 +943,9 @@ msgstr "" msgid "Speed monitoring — conntrack byte counters are not updated" msgstr "" +msgid "Split up/down" +msgstr "" + msgid "State" msgstr "" @@ -917,20 +955,25 @@ msgstr "" msgid "Stays off until you switch it back." msgstr "" -msgid "" -"Store the current Poll and Window as this router's defaults, for browsers that h" -"ave not set their own" +msgid "Store" +msgstr "" + +msgid "Store the current Poll and Window as this router's defaults, for browsers that have not set their own" msgstr "" msgid "Switch it off" msgstr "" +msgid "Switch off" +msgstr "" + +msgid "Switch on" +msgstr "" + msgid "Syslog" msgstr "" -msgid "" -"TCP bytes transferred, accumulated on the router. Not measurable while flow offl" -"oad is active" +msgid "TCP bytes transferred, accumulated on the router. Not measurable while flow offload is active" msgstr "" msgid "TCP: %d" @@ -951,25 +994,34 @@ msgstr "" msgid "Test" msgstr "" -msgid "" -"The auto-revert helper is not running; this page re-checks the deadline while it" -" is open." +msgid "The accuracy of these figures could not be determined." +msgstr "" + +msgid "The auto-revert helper is not running; this page re-checks the deadline while it is open." msgstr "" msgid "The flowtable" msgstr "" +msgid "The flowtable carries the" +msgstr "" + msgid "The internet cut is switched on, but its firewall rule is missing." msgstr "" -msgid "" -"The kernel fast-paths established connections through a flowtable, bypassing con" -"ntrack byte updates for higher throughput." +msgid "The kernel fast-paths established connections through a flowtable for higher throughput." msgstr "" -msgid "" -"The router offloads established connections from the CPU to the hardware (NIC/So" -"C), achieving 2–3× higher throughput and lower CPU usage." +msgid "The kernel fast-paths established connections through a flowtable, bypassing conntrack byte updates for higher throughput." +msgstr "" + +msgid "The router offloads established connections from the CPU to the hardware (NIC/SoC), achieving 2–3× higher throughput and lower CPU usage." +msgstr "" + +msgid "The store directory is not writable yet." +msgstr "" + +msgid "This build of trafficctl does not have a description for the mode the router reported, so how it affects monitoring is not stated here rather than guessed at." msgstr "" msgid "Throttle removed" @@ -993,9 +1045,10 @@ msgstr "" msgid "Top talkers" msgstr "" -msgid "" -"Total bytes transferred (download + upload), accumulated on the router. Resets o" -"n reboot; hover a cell for the start time" +msgid "Total" +msgstr "" + +msgid "Total bytes transferred (download + upload), accumulated on the router. Resets on reboot; hover a cell for the start time" msgstr "" msgid "Total dropped" @@ -1013,9 +1066,13 @@ msgstr "" msgid "Traffic Control" msgstr "" -msgid "" -"Traffic is flowing right now. It is normally re-asserted within seconds — if thi" -"s persists, switch it off and on again." +msgid "Traffic History" +msgstr "" + +msgid "Traffic history is switched off. Nothing is being recorded." +msgstr "" + +msgid "Traffic is flowing right now. It is normally re-asserted within seconds — if this persists, switch it off and on again." msgstr "" msgid "Traffic shaping (tc/HTB) — bypassed for offloaded flows" @@ -1024,9 +1081,7 @@ msgstr "" msgid "Traffic shaping and blocking are not affected." msgstr "" -msgid "" -"UDP bytes transferred, accumulated on the router. Not measurable while flow offl" -"oad is active" +msgid "UDP bytes transferred, accumulated on the router. Not measurable while flow offload is active" msgstr "" msgid "UDP: %d" @@ -1050,6 +1105,9 @@ msgstr "" msgid "Until I switch it back" msgstr "" +msgid "Upload" +msgstr "" + msgid "Upload · %s" msgstr "" @@ -1071,6 +1129,12 @@ msgstr "" msgid "Waiting for the first byte sample." msgstr "" +msgid "When this device last moved any traffic, not when it was last connected" +msgstr "" + +msgid "While this total was being accumulated, flow offloading froze the counters it is built from. The figure is a floor, not a measurement." +msgstr "" + msgid "Whole network" msgstr "" @@ -1101,6 +1165,9 @@ msgstr "" msgid "Window" msgstr "" +msgid "Write the figures accumulated so far to the store now, without waiting for the next scheduled flush" +msgstr "" + msgid "You will need LAN or physical access to undo this — a reboot will not clear it." msgstr "" @@ -1110,9 +1177,15 @@ msgstr "" msgid "all active blocks, limits, and shapes" msgstr "" +msgid "at least" +msgstr "" + msgid "between them — one bucket for the whole subnet." msgstr "" +msgid "between them — one bucket per direction for the whole subnet." +msgstr "" + msgid "bit/s" msgstr "" @@ -1146,6 +1219,9 @@ msgstr "" msgid "date + time" msgstr "" +msgid "days ago" +msgstr "" + msgid "device connections" msgstr "" @@ -1155,6 +1231,9 @@ msgstr "" msgid "device list → tap device → action buttons" msgstr "" +msgid "devices" +msgstr "" + msgid "disabled" msgstr "" @@ -1173,16 +1252,16 @@ msgstr "" msgid "failed" msgstr "" -msgid "" -"flag (Linux 5.7+, set automatically by fw4/nftables) periodically syncs hardware" -" byte counts back to conntrack — trafficctl detects this and all features work n" -"ormally." +msgid "flag (Linux 5.7+, set automatically by fw4), so offloaded byte counts are synced back to conntrack. Speed and totals come from conntrack as usual and are accurate — the nftables counter fallback is not needed on this router." msgstr "" -msgid "" -"flag should sync hardware byte counts back to conntrack, but on many platforms (" -"e.g. Mediatek Filogic) the driver does not implement the stats callback, so conn" -"track counters remain frozen for active flows." +msgid "flag (Linux 5.7+, set automatically by fw4/nftables) periodically syncs hardware byte counts back to conntrack — trafficctl detects this and all features work normally." +msgstr "" + +msgid "flag should sync hardware byte counts back to conntrack, but on many platforms (e.g. Mediatek Filogic) the driver does not implement the stats callback, so conntrack counters remain frozen for active flows." +msgstr "" + +msgid "flush every" msgstr "" msgid "groups from" @@ -1191,16 +1270,24 @@ msgstr "" msgid "h" msgstr "" -msgid "" -"is not one of the subnets this router monitors, so a limit on it would be accept" -"ed but never enforced. A firewall zone other than \"lan\" with masq=1 (the usual g" -"uest-VLAN setup) is excluded. Check with: uci show firewall | grep -E \"name=|mas" -"q=\"" +msgid "h ago" +msgstr "" + +msgid "is not one of the subnets this router monitors, so a limit on it would be accepted but never enforced. A firewall zone other than \"lan\" with masq=1 (the usual guest-VLAN setup) is excluded. Check with: uci show firewall | grep -E \"name=|masq=\"" +msgstr "" + +msgid "just now" msgstr "" msgid "kbit/s" msgstr "" +msgid "keeping" +msgstr "" + +msgid "last flush" +msgstr "" + msgid "line break" msgstr "" @@ -1210,6 +1297,9 @@ msgstr "" msgid "min" msgstr "" +msgid "min ago" +msgstr "" + msgid "network interface" msgstr "" @@ -1249,6 +1339,12 @@ msgstr "" msgid "percentage of total traffic that was dropped" msgstr "" +msgid "period" +msgstr "" + +msgid "periods" +msgstr "" + msgid "pkts" msgstr "" @@ -1282,6 +1378,9 @@ msgstr "" msgid "s" msgstr "" +msgid "sampled every" +msgstr "" + msgid "saved" msgstr "" @@ -1312,26 +1411,23 @@ msgstr "" msgid "under a minute" msgstr "" +msgid "undercounted" +msgstr "" + msgid "value" msgstr "" msgid "↑ UL" msgstr "" -msgid "" -"↓ / ↑ are relative to the interface: on the WAN ↓ is your download, on a LAN bri" -"dge ↓ is what the LAN sent upstream." +msgid "↑ Upload" +msgstr "" + +msgid "↓ / ↑ are relative to the interface: on the WAN ↓ is your download, on a LAN bridge ↓ is what the LAN sent upstream." msgstr "" msgid "↓ DL" msgstr "" -msgid "" -"🆕 New device\n" -"{{ name }} ({{ ip }})\n" -"MAC: {{ mac }}\n" -"Link: {{ link }}\n" -msgstr "" - -msgid "🆕 New device\\n{{ name }} ({{ ip }})\\nMAC: {{ mac }}\\nLink: {{ link }}" +msgid "✕" msgstr "" diff --git a/luci-app-trafficctl/po/zh_Hans/luci-app-trafficctl.po b/luci-app-trafficctl/po/zh_Hans/luci-app-trafficctl.po index 4c58f7202..cd7fe1e19 100644 --- a/luci-app-trafficctl/po/zh_Hans/luci-app-trafficctl.po +++ b/luci-app-trafficctl/po/zh_Hans/luci-app-trafficctl.po @@ -758,8 +758,8 @@ msgstr "队列" msgid "Queue memory" msgstr "队列内存" -msgid "Queued: %s" -msgstr "队列中:%s" +msgid "Queued: %s" +msgstr "队列中:%s" msgid "Queues excess traffic (smoother streaming, lower jitter)" msgstr "对超额流量排队(更流畅流媒体、更低抖动)" @@ -863,8 +863,8 @@ msgstr "已整形设备通常不会被卸载(内核会检测到 HTB qdisc) msgid "Shaped:" msgstr "已整形:" -msgid "Shaped: 🌊 %s" -msgstr "已整形:🌊 %s" +msgid "Shaped: 🌊 %s" +msgstr "已整形:🌊 %s" msgid "Shaper" msgstr "整形器" @@ -913,8 +913,8 @@ msgid "" "he flowtable), so graphs are accurate." msgstr "速度通过安装在更高优先级(flowtable 之前)的 nftables 计数器测量,因此图表准确。" -msgid "Speed limit: ⚡ %s" -msgstr "速度限制:⚡ %s" +msgid "Speed limit: ⚡ %s" +msgstr "速度限制:⚡ %s" msgid "Speed limit: shaper (queue) or limiter (drop)" msgstr "速度限制:整形器(队列)或限速器(丢包)" diff --git a/mise/Makefile b/mise/Makefile index 790920ba9..aa2a9a5a6 100644 --- a/mise/Makefile +++ b/mise/Makefile @@ -14,8 +14,8 @@ MISE_HASH_x86_64:=3832f39c325e343f81fe3d92b2447c5d1a5eea1bc85092bb7b6c2580622264 MISE_HASH_aarch64:=06186cfbfe947049b21d58575fb0ea800cc26ed1375f20f4b678cb3a9d679437 PKG_NAME:=mise -PKG_VERSION:=2026.10.15 -PKG_RELEASE:=39 +PKG_VERSION:=2026.10.17 +PKG_RELEASE:=40 PKG_SOURCE:=$(PKG_NAME)-v$(PKG_VERSION)-linux-$(MISE_ARCH)-musl.tar.gz PKG_SOURCE_URL:=https://github.com/jdx/mise/releases/download/v$(PKG_VERSION)/ PKG_HASH:=skip diff --git a/smartdns/Makefile b/smartdns/Makefile index d3e0ecf96..bea0ac88c 100644 --- a/smartdns/Makefile +++ b/smartdns/Makefile @@ -1,58 +1,64 @@ +# SPDX-License-Identifier: GPL-3.0-or-later # -# Copyright (c) 2018-2026 Nick Peng (pymumu@gmail.com) -# This is free software, licensed under the GNU General Public License v3. -# +# Copyright (c) 2018-2023 Nick Peng (pymumu@gmail.com) include $(TOPDIR)/rules.mk PKG_NAME:=smartdns -PKG_VERSION:=1.2026.48.4 -PKG_RELEASE:=7 +PKG_VERSION:=48.4 +PKG_RELEASE:=8 -PKG_SOURCE_PROTO:=git -PKG_SOURCE_URL:=https://www.github.com/pymumu/smartdns.git -PKG_MIRROR_HASH:=skip -PKG_SOURCE_VERSION:=21c940edc65520849ba03544c5cf8d9cf326e680 - -SMARTDNS_WEBUI_VERSION:=1.0.0 -SMARTDNS_WEBUI_SOURCE_PROTO:=git -SMARTDNS_WEBUI_SOURCE_URL:=https://github.com/pymumu/smartdns-webui.git -SMARTDNS_WEBUI_SOURCE_VERSION:=1c06fee693434ec7c71f06010d3f87416baba9f0 -SMARTDNS_WEBUI_FILE:=smartdns-webui-$(SMARTDNS_WEBUI_VERSION).tar.gz +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/pymumu/smartdns/tar.gz/Release$(PKG_VERSION)? +PKG_HASH:=skip +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-Release$(PKG_VERSION) PKG_MAINTAINER:=Nick Peng PKG_LICENSE:=GPL-3.0-or-later PKG_LICENSE_FILES:=LICENSE +PKG_CPE_ID:=cpe:/a:pymumu:smartdns +PKG_CONFIG_DEPENDS:=CONFIG_PACKAGE_smartdns-ui +PKG_BUILD_DEPENDS:=PACKAGE_smartdns-ui:node/host PACKAGE_smartdns-ui:rust-bindgen/host PKG_BUILD_PARALLEL:=1 -# node compile is slow, so do not use it, download node manually. -# PACKAGE_smartdns-ui:node/host -PKG_BUILD_DEPENDS:=PACKAGE_smartdns-ui:rust/host +RUST_PKG_FEATURES:=build-release +RUST_PKG_LOCKED:=0 -include $(TOPDIR)/feeds/packages/lang/rust/rust-package.mk include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/rust/rust-package.mk -MAKE_VARS += VER=$(PKG_VERSION) MAKE_PATH:=src +MAKE_VARS+= VER=$(PKG_VERSION) WITH_ZLIB=0 -define Package/smartdns/default +define Package/smartdns/Default SECTION:=net CATEGORY:=Network SUBMENU:=IP Addresses and Names + TITLE:=smartdns URL:=https://www.github.com/pymumu/smartdns/ endef define Package/smartdns - $(Package/smartdns/default) - TITLE:=smartdns server - DEPENDS:=+libpthread +libopenssl +libatomic +zlib + $(call Package/smartdns/Default) + TITLE+= server + DEPENDS:=+i386:libatomic +libopenssl endef define Package/smartdns/description SmartDNS is a local DNS server which accepts DNS query requests from local network clients, gets DNS query results from multiple upstream DNS servers concurrently, and returns the fastest IP to clients. -Unlike dnsmasq's all-servers, smartdns returns the fastest IP, and encrypt DNS queries with DoT or DoH. +Unlike dnsmasq's all-servers, smartdns returns the fastest IP, and encrypt DNS queries with DoT or DoH. +endef + +define Package/smartdns-ui + $(call Package/smartdns/Default) + TITLE+= dashboard + DEPENDS:=+smartdns $(RUST_ARCH_DEPENDS) @!i386 +endef + +define Package/smartdns-ui/description +A dashboard ui for smartdns server. endef define Package/smartdns/conffiles @@ -64,80 +70,73 @@ define Package/smartdns/conffiles /etc/smartdns/domain-forwarding.list endef +define Download/smartdns-webui + PROTO:=git + URL:=https://github.com/pymumu/smartdns-webui.git + SOURCE_DATE:=2026-07-29 + SOURCE_VERSION:=1c06fee693434ec7c71f06010d3f87416baba9f0 + MIRROR_HASH:=1b718d1288f6ddbdd80e3a948dda1693fd7bc8079fdb65be121f64f23ce53849 + SUBDIR:=smartdns-webui-$$$$(subst -,.,$$$$(SOURCE_DATE))~$$$$(call version_abbrev,$$$$(SOURCE_VERSION)) + FILE:=$$(SUBDIR).tar.zst +endef + +define Build/Prepare + $(call Build/Prepare/Default) + +ifneq ($(CONFIG_PACKAGE_smartdns-ui),) + $(eval $(call Download,smartdns-webui)) + $(eval $(Download/smartdns-webui)) + mkdir -p $(PKG_BUILD_DIR)/smartdns-webui + zstdcat $(DL_DIR)/$(FILE) | tar -C $(PKG_BUILD_DIR)/smartdns-webui $(TAR_OPTIONS) --strip-components=1 +endif +endef + +define Build/Compile + $(call Build/Compile/Default) + +ifneq ($(CONFIG_PACKAGE_smartdns-ui),) + ( \ + pushd $(PKG_BUILD_DIR) ; \ + pushd smartdns-webui ; \ + npm install ; \ + npm run build ; \ + popd ; \ + pushd plugin/smartdns-ui ; \ + $(CARGO_PKG_CONFIG_VARS) \ + MAKEFLAGS="$(PKG_JOBS)" \ + WITH_ZLIB=0 \ + TARGET_CFLAGS="$(filter-out -O%,$(TARGET_CFLAGS)) $(RUSTC_CFLAGS)" \ + BINDGEN_EXTRA_CLANG_ARGS="--sysroot=$(TOOLCHAIN_ROOT_DIR)" \ + cargo build -v --profile $(CARGO_PKG_PROFILE) \ + $(if $(strip $(RUST_PKG_FEATURES)),--features "$(strip $(RUST_PKG_FEATURES))") \ + $(if $(filter --jobserver%,$(PKG_JOBS)),,-j1) \ + $(CARGO_PKG_ARGS) ; \ + popd ; \ + popd ; \ + ) +endif +endef + define Package/smartdns/install - $(INSTALL_DIR) $(1)/usr/sbin $(1)/etc/config $(1)/etc/init.d + $(INSTALL_DIR) $(1)/usr/sbin $(1)/etc/config $(1)/etc/init.d $(INSTALL_DIR) $(1)/etc/smartdns $(1)/etc/smartdns/domain-set $(1)/etc/smartdns/conf.d/ + $(INSTALL_DIR) $(1)/etc/smartdns/ip-set $(1)/etc/smartdns/download $(INSTALL_BIN) $(PKG_BUILD_DIR)/src/smartdns $(1)/usr/sbin/smartdns $(INSTALL_BIN) $(PKG_BUILD_DIR)/package/openwrt/files/etc/init.d/smartdns $(1)/etc/init.d/smartdns $(INSTALL_CONF) $(PKG_BUILD_DIR)/package/openwrt/address.conf $(1)/etc/smartdns/address.conf $(INSTALL_CONF) $(PKG_BUILD_DIR)/package/openwrt/blacklist-ip.conf $(1)/etc/smartdns/blacklist-ip.conf $(INSTALL_CONF) $(PKG_BUILD_DIR)/package/openwrt/custom.conf $(1)/etc/smartdns/custom.conf + $(INSTALL_CONF) $(PKG_BUILD_DIR)/package/openwrt/domain-block.list $(1)/etc/smartdns/domain-block.list + $(INSTALL_CONF) $(PKG_BUILD_DIR)/package/openwrt/domain-forwarding.list $(1)/etc/smartdns/domain-forwarding.list $(INSTALL_CONF) $(PKG_BUILD_DIR)/package/openwrt/files/etc/config/smartdns $(1)/etc/config/smartdns endef -define Package/smartdns-ui - $(Package/smartdns/default) - TITLE:=smartdns dashboard - DEPENDS:=+smartdns $(RUST_ARCH_DEPENDS) -endef - -define Package/smartdns-ui/description -A dashboard ui for smartdns server. -endef - -define Package/smartdns-ui/conffiles -/etc/config/smartdns -endef - define Package/smartdns-ui/install $(INSTALL_DIR) $(1)/usr/lib - $(INSTALL_DIR) $(1)/etc/smartdns/conf.d/ - $(INSTALL_DIR) $(1)/usr/share/smartdns/wwwroot - $(INSTALL_BIN) $(PKG_BUILD_DIR)/plugin/smartdns-ui/target/smartdns_ui.so $(1)/usr/lib/smartdns_ui.so - $(CP) $(PKG_BUILD_DIR)/smartdns-webui/out/* $(1)/usr/share/smartdns/wwwroot -endef - -define Build/Compile/smartdns-webui - which npm || (echo "npm not found, please install npm first" && exit 1) - npm install --prefix $(PKG_BUILD_DIR)/smartdns-webui/ - npm run build --prefix $(PKG_BUILD_DIR)/smartdns-webui/ -endef - -define Build/Compile/smartdns-ui - cargo install --force --locked bindgen-cli - CARGO_BUILD_ARGS="$(if $(strip $(RUST_PKG_FEATURES)),--features "$(strip $(RUST_PKG_FEATURES))") --profile $(CARGO_PKG_PROFILE)" - +$(CARGO_PKG_VARS) CARGO_BUILD_ARGS="$(CARGO_BUILD_ARGS)" CC=$(TARGET_CC) \ - PATH="$$(PATH):$(CARGO_HOME)/bin" \ - AWS_LC_SYS_CFLAGS="-O0" \ - make -C $(PKG_BUILD_DIR)/plugin/smartdns-ui -endef - -define Download/smartdns-webui - FILE:=$(SMARTDNS_WEBUI_FILE) - PROTO:=$(SMARTDNS_WEBUI_SOURCE_PROTO) - URL:=$(SMARTDNS_WEBUI_SOURCE_URL) - MIRROR_HASH:=88adb092ed7b0dc22b3964249f4969870d3b462c4d924aceb18784c0f664f16e - VERSION:=$(SMARTDNS_WEBUI_SOURCE_VERSION) - HASH:=skip - SUBDIR:=smartdns-webui -endef -$(eval $(call Download,smartdns-webui)) - -ifdef CONFIG_PACKAGE_smartdns-ui -define Build/Prepare - $(call Build/Prepare/Default) - $(TAR) -C $(PKG_BUILD_DIR)/ -xf $(DL_DIR)/$(SMARTDNS_WEBUI_FILE) -endef -endif - -define Build/Compile - $(call Build/Compile/Default,smartdns) -ifdef CONFIG_PACKAGE_smartdns-ui - $(call Build/Compile/smartdns-ui) - $(call Build/Compile/smartdns-webui) -endif + $(INSTALL_DIR) $(1)/usr/share/smartdns + $(CP) $(PKG_BUILD_DIR)/plugin/smartdns-ui/target/$(RUSTC_TARGET_ARCH)/$(CARGO_PKG_PROFILE)/libsmartdns_ui.so $(1)/usr/lib/smartdns_ui.so + $(CP) $(PKG_BUILD_DIR)/smartdns-webui/out $(1)/usr/share/smartdns/wwwroot endef $(eval $(call BuildPackage,smartdns)) -$(eval $(call RustBinPackage,smartdns-ui)) -$(eval $(call BuildPackage,smartdns-ui)) \ No newline at end of file +$(eval $(call BuildPackage,smartdns-ui))