diff --git a/luci-app-fchomo/htdocs/luci-static/resources/fchomo/listeners.js b/luci-app-fchomo/htdocs/luci-static/resources/fchomo/listeners.js index 95482189..be9b03fb 100644 --- a/luci-app-fchomo/htdocs/luci-static/resources/fchomo/listeners.js +++ b/luci-app-fchomo/htdocs/luci-static/resources/fchomo/listeners.js @@ -652,12 +652,16 @@ function renderListeners(s, uciconfig, isClient) { const type = this.section.getOption('type').formvalue(section_id); if (value) { - if (type === 'snell' && !['obfs', 'shadow-tls'].includes(value)) { + if (type === 'snell' && !['obfs', 'shadow-tls', 'restls', 'jls'].includes(value)) { return _('Expecting: Only support %s.').format(_('obfs-simple') + - ' / ' + _('ShadowTLS')); + ' / ' + _('ShadowTLS') + + ' / ' + _('Restls') + + ' / ' + _('JLS')); } - if (['vmess', 'vless', 'trojan', 'anytls'].includes(type) && !['jls'].includes(value)) { - return _('Expecting: Only support %s.').format(_('JLS')); + if (['vmess', 'vless', 'trojan', 'anytls'].includes(type) && !['shadow-tls', 'restls', 'jls'].includes(value)) { + return _('Expecting: only support %s.').format(_('ShadowTLS') + + ' / ' + _('Restls') + + ' / ' + _('JLS')); } } @@ -726,7 +730,7 @@ function renderListeners(s, uciconfig, isClient) { ...hm.loadLabelValues(this.config, 'proxy_group') ], section_id); } - o.depends({plugin_type: /^(restls|jls)$/}); + o.depends({plugin_type: /^(shadow-tls|restls|jls)$/}); o.depends({type: 'shadowquic'}); o.modalonly = true; } @@ -1033,8 +1037,8 @@ function renderListeners(s, uciconfig, isClient) { o = s.taboption('field_tls', form.Value, 'tls_sni', _('TLS SNI'), _('Hostname that the client attempts to connect to at the start of the TLS handshake process.')); - o.depends('plugin_type', 'jls'); o.depends({tls: '1', type: 'shadowquic'}); + o.depends('plugin_type', 'jls'); o.modalonly = true; o = s.taboption('field_tls', form.DynamicList, 'tls_alpn', _('TLS ALPN'), @@ -1092,9 +1096,11 @@ function renderListeners(s, uciconfig, isClient) { const plugin_type = this.section.getOption('plugin_type').formvalue(section_id); const tls_reality = this.section.getOption('tls_reality').formvalue(section_id); - if (plugin_type === 'jls' || tls_reality == 1) { + if (['shadow-tls', 'restls', 'jls'].includes(plugin_type) || tls_reality == 1) { if (value) - return _('Expecting: Keep empty when %s is enabled.').format(_('JLS') + + return _('Expecting: Keep empty when %s is enabled.').format(_('ShadowTLS') + + ' / ' + _('Restls') + + ' / ' + _('JLS') + ' / ' + _('REALITY')); } else if (!value) { return _('Expecting: Cannot be empty.'); @@ -1207,8 +1213,10 @@ function renderListeners(s, uciconfig, isClient) { const plugin_type = this.section.getOption('plugin_type').formvalue(section_id); value = this.formvalue(section_id); - if (value == 1 && plugin_type === 'jls') - return _('Expecting: Cannot be enabled when %s is enabled.').format(_('JLS')); + if (value == 1 && ['shadow-tls', 'restls', 'jls'].includes(plugin_type)) + return _('Expecting: cannot be enabled when %s is enabled.').format(_('ShadowTLS') + + ' / ' + _('Restls') + + ' / ' + _('JLS')); return true; } diff --git a/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/node.js b/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/node.js index 31c65b9a..d90ddb4a 100644 --- a/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/node.js +++ b/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/node.js @@ -104,7 +104,7 @@ const parseProviderYaml = hm.parseYaml.extend({ if (!cfg.type) return null; - // key mapping // 2026/07/12 + // key mapping // 2026/07/18 let config = hm.removeBlankAttrs({ id: this.id, label: this.label, @@ -968,6 +968,7 @@ return view.extend({ so = ss.taboption('field_general', form.Flag, 'plugin', _('Plugin')); so.default = so.disabled; so.depends({type: /^(ss|snell)$/}); + so.depends({type: /^(vmess|vless|trojan|anytls)$/}); so.modalonly = true; so = ss.taboption('field_plugin', form.ListValue, 'plugin_type', _('Plugin type')); @@ -982,9 +983,16 @@ return view.extend({ const type = this.section.getOption('type').formvalue(section_id); if (value) { - if (type === 'snell' && !['obfs', 'shadow-tls'].includes(value)) { + if (type === 'snell' && !['obfs', 'shadow-tls', 'restls', 'jls'].includes(value)) { return _('Expecting: Only support %s.').format(_('obfs-simple') + - ' / ' + _('ShadowTLS')); + ' / ' + _('ShadowTLS') + + ' / ' + _('Restls') + + ' / ' + _('JLS')); + } + if (['vmess', 'vless', 'trojan', 'anytls'].includes(type) && !['shadow-tls', 'restls', 'jls'].includes(value)) { + return _('Expecting: only support %s.').format(_('ShadowTLS') + + ' / ' + _('Restls') + + ' / ' + _('JLS')); } } @@ -1028,6 +1036,8 @@ return view.extend({ so = ss.taboption('field_plugin', form.Value, 'plugin_opts_restls_versionhint', _('Version hint')); so.default = 'tls13'; + so.value('tls12', _('tls12')); + so.value('tls13', _('tls13')); so.rmempty = false; so.depends({plugin_type: 'restls'}); so.modalonly = true; @@ -1167,14 +1177,6 @@ return view.extend({ so = ss.taboption('field_tls', form.Value, 'tls_sni', _('TLS SNI'), _('Hostname that the client attempts to connect to at the start of the TLS handshake process.')); - so.validate = function(section_id, value) { - const tls_jls = this.section.getOption('tls_jls').formvalue(section_id); - - if (tls_jls == 1 && !value) - return _('Expecting: %s cannot be empty when %s is enabled.').format(_('TLS SNI'), _('JLS')); - - return true; - }; so.depends({tls: '1', type: /^(http|vmess|vless|trojan|anytls|hysteria|hysteria2|shadowquic|trusttunnel|masque)$/}); so.depends({tls: '1', type: /^(tuic)$/, tls_disable_sni: '0'}); so.modalonly = true; @@ -1222,8 +1224,8 @@ return view.extend({ return true; } so.depends({tls: '1', type: /^(vmess|vless|trojan|anytls|tuic|hysteria|hysteria2|shadowquic|trusttunnel)$/}); - so.depends({type: /^(ss|snell)$/, plugin_type: 'shadow-tls'}); - so.depends({type: 'ss', plugin_type: 'jls'}); + so.depends({type: 'ss', plugin_type: /^(shadow-tls|jls)$/}); + so.depends({type: 'snell', plugin_type: 'shadow-tls'}); so.modalonly = true; so = ss.taboption('field_tls', form.Value, 'tls_fingerprint', _('Cert fingerprint'), @@ -1305,28 +1307,23 @@ return view.extend({ so.value.apply(so, res); }) so.depends({tls: '1', type: /^(vmess|vless|trojan|anytls|trusttunnel)$/}); - so.depends({type: /^(ss|snell)$/, plugin_type: /^(shadow-tls|restls)$/}); - so.depends({type: 'ss', plugin_type: 'jls'}); - so.modalonly = true; - - so = ss.taboption('field_tls', form.Flag, 'tls_jls', _('JLS')); - so.default = so.disabled; - so.depends({tls: '1', type: /^(vmess|vless|trojan|anytls)$/}); - so.modalonly = true; - - so = ss.taboption('field_tls', form.Value, 'tls_jls_username', _('JLS username')); - so.rmempty = false; - so.depends('tls_jls', '1'); - so.modalonly = true; - - so = ss.taboption('field_tls', form.Value, 'tls_jls_password', _('JLS password')); - so.rmempty = false; - so.depends('tls_jls', '1'); + so.depends({type: /^(ss|snell)$/, plugin_type: /^(shadow-tls|restls|jls)$/}); so.modalonly = true; so = ss.taboption('field_tls', form.Flag, 'tls_reality', _('REALITY')); so.default = so.disabled; - so.depends({tls: '1', tls_jls: '0', type: /^(vmess|vless|trojan)$/}); + so.validate = function(section_id, value) { + const plugin_type = this.section.getOption('plugin_type').formvalue(section_id); + value = this.formvalue(section_id); + + if (value == 1 && ['shadow-tls', 'restls', 'jls'].includes(plugin_type)) + return _('Expecting: cannot be enabled when %s is enabled.').format(_('ShadowTLS') + + ' / ' + _('Restls') + + ' / ' + _('JLS')); + + return true; + } + so.depends({tls: '1', type: /^(vmess|vless|trojan)$/}); so.modalonly = true; so = ss.taboption('field_tls', form.Value, 'tls_reality_public_key', _('REALITY public key')); @@ -1985,7 +1982,7 @@ return view.extend({ so = ss.taboption('field_override', form.DynamicList, 'override_expr', _('Programmable replacement'), _('For format see %s.') - .format('https://wiki.metacubex.one/config/proxy-providers/#override_expr', _('override.override-expr'))); + .format('https://wiki.metacubex.one/config/proxy-providers/#overrideoverride-expr', _('override.override-expr'))); so.placeholder = '.name = "[provider1] " + .name'; so.depends({type: 'inline', '!reverse': true}); so.modalonly = true; diff --git a/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_inbound b/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_inbound index 6b67df3c..f2310afd 100755 --- a/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_inbound +++ b/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_inbound @@ -50,7 +50,7 @@ migrate() { fi # plugin -> plugin_type - if isDefined "$1" plugin; then + if isDefined "$1" plugin && ! isDefined "$1" plugin_type; then local plugin config_get plugin "$1" plugin "" uci_set "$CONF" "$1" plugin "1" diff --git a/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_node b/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_node index 4bf0acd1..31c9c278 100755 --- a/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_node +++ b/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_node @@ -76,7 +76,7 @@ migrate() { fi # plugin -> plugin_type - if isDefined "$1" plugin; then + if isDefined "$1" plugin && ! isDefined "$1" plugin_type; then local plugin config_get plugin "$1" plugin "" uci_set "$CONF" "$1" plugin "1" diff --git a/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_server b/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_server index 1dd0769e..7eef0210 100755 --- a/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_server +++ b/luci-app-fchomo/root/etc/uci-defaults/99_luci-app-fchomo-migration_server @@ -58,7 +58,7 @@ migrate() { fi # plugin -> plugin_type - if isDefined "$1" plugin; then + if isDefined "$1" plugin && ! isDefined "$1" plugin_type; then local plugin config_get plugin "$1" plugin "" uci_set "$CONF" "$1" plugin "1" diff --git a/luci-app-fchomo/root/usr/share/fchomo/generate_client.uc b/luci-app-fchomo/root/usr/share/fchomo/generate_client.uc index 13b0e6a7..fe9957ce 100644 --- a/luci-app-fchomo/root/usr/share/fchomo/generate_client.uc +++ b/luci-app-fchomo/root/usr/share/fchomo/generate_client.uc @@ -345,6 +345,8 @@ uci.foreach(uciconf, uciinbd, (cfg) => { const listener = parseListener(cfg); listener.proxy = get_proxy(listener.proxy); + if (listener["shadow-tls"]) + listener["shadow-tls"].handshake.proxy = get_proxy(listener["shadow-tls"].handshake.proxy); if (listener["res-tls"]) listener["res-tls"].proxy = get_proxy(listener["res-tls"].proxy); if (listener["jls-config"]) @@ -541,7 +543,7 @@ uci.foreach(uciconf, ucinode, (cfg) => { /* Snell */ psk: cfg.snell_psk, - version: cfg.snell_version, + version: strToInt(cfg.snell_version), reuse: strToBool(cfg.snell_reuse), /* VMess / VLESS */ @@ -641,38 +643,49 @@ uci.foreach(uciconf, ucinode, (cfg) => { /* Plugin fields */ ...(cfg.plugin === '1' ? ( - cfg.type === 'snell' ? { - // snell - "obfs-opts": { - mode: cfg.plugin_type === 'shadow-tls' ? cfg.plugin_type : cfg.plugin_opts_obfsmode, - host: cfg.plugin_opts_host, - password: cfg.plugin_opts_thetlspassword, + cfg.type in ['vmess', 'vless', 'trojan', 'anytls'] ? { + ...arrToObj([[(cfg.type in ['vmess', 'vless']) ? 'servername' : 'sni', cfg.plugin_opts_host]]), + // shadow-tls + "shadow-tls-opts": cfg.plugin_type === 'shadow-tls' ? { version: strToInt(cfg.plugin_opts_shadowtls_version), - alpn: cfg.tls_alpn // Array - } - } : { - // others - plugin: cfg.plugin_type, - "plugin-opts": { - mode: cfg.plugin_opts_obfsmode, - host: cfg.plugin_opts_host, - username: cfg.plugin_opts_thetlsusername, + password: cfg.plugin_opts_thetlspassword + } : null, + // restls + "restls-opts": cfg.plugin_type === 'restls' ? { password: cfg.plugin_opts_thetlspassword, - version: strToInt(cfg.plugin_opts_shadowtls_version), - alpn: cfg.tls_alpn, // Array "version-hint": cfg.plugin_opts_restls_versionhint, "restls-script": cfg.plugin_opts_restls_script - } + } : null, + // jls + "jls-opts": cfg.plugin_type === 'jls' ? { + username: cfg.plugin_opts_thetlsusername, + password: cfg.plugin_opts_thetlspassword + } : null, + } : { + // snell / shadowsocks + plugin: cfg.type === 'snell' ? null : cfg.plugin_type, + ...arrToObj([[cfg.type === 'snell' ? "obfs-opts" : "plugin-opts", + { + mode: (cfg.type === 'snell' && cfg.plugin_type !== 'obfs') ? cfg.plugin_type : cfg.plugin_opts_obfsmode, + host: cfg.plugin_opts_host, + username: cfg.plugin_opts_thetlsusername, + password: cfg.plugin_opts_thetlspassword, + version: strToInt(cfg.plugin_opts_shadowtls_version), + alpn: cfg.tls_alpn, // Array + "version-hint": cfg.plugin_opts_restls_versionhint, + "restls-script": cfg.plugin_opts_restls_script + } + ]]) } ) : {}), /* SSH / WireGuard / Masque */ /* TLS fields */ - tls: (cfg.type in ['trojan', 'anytls', 'tuic', 'hysteria', 'hysteria2', 'shadowquic', 'trusttunnel']) ? null : strToBool(cfg.tls), + tls: (cfg.type in ['trojan', 'anytls', 'tuic', 'hysteria', 'hysteria2', 'shadowquic', 'trusttunnel', 'masque']) ? null : strToBool(cfg.tls), "disable-sni": strToBool(cfg.tls_disable_sni), - ...arrToObj([[(cfg.type in ['vmess', 'vless']) ? 'servername' : 'sni', cfg.tls_sni]]), + ...(cfg.tls_sni ? arrToObj([[(cfg.type in ['vmess', 'vless']) ? 'servername' : 'sni', cfg.tls_sni]]) : {}), fingerprint: cfg.tls_fingerprint, - alpn: cfg.plugin_type in ['shadow-tls', 'jls'] ? null : cfg.tls_alpn, // Array + alpn: cfg.tls === '1' ? cfg.tls_alpn : null, // Array "name-cert-verify": cfg.tls_name_cert_verify, "skip-cert-verify": strToBool(cfg.tls_skip_cert_verify), certificate: cfg.tls_cert_path, // mTLS @@ -684,10 +697,6 @@ uci.foreach(uciconf, ucinode, (cfg) => { config: cfg.tls_ech_config, "query-server-name": cfg.tls_ech_query_server_name } : null, - "jls-opts": cfg.tls_jls === '1' ? { - "username": cfg.tls_jls_username, - "password": cfg.tls_jls_password - } : null, "reality-opts": cfg.tls_reality === '1' ? { "public-key": cfg.tls_reality_public_key, "short-id": cfg.tls_reality_short_id, diff --git a/luci-app-fchomo/root/usr/share/ucode/fchomo.uc b/luci-app-fchomo/root/usr/share/ucode/fchomo.uc index dc4c4c4d..285b0d79 100644 --- a/luci-app-fchomo/root/usr/share/ucode/fchomo.uc +++ b/luci-app-fchomo/root/usr/share/ucode/fchomo.uc @@ -385,7 +385,8 @@ export function parseListener(cfg) { ], } : { password: cfg.plugin_opts_thetlspassword }), handshake: { - dest: cfg.plugin_opts_handshake_dest + dest: cfg.plugin_opts_handshake_dest, + proxy: cfg.plugin_opts_dest_proxy // raw data need post-processing } } } : cfg.plugin_type === 'restls' ? { diff --git a/mihomo/Makefile b/mihomo/Makefile index 8848814f..04686e65 100644 --- a/mihomo/Makefile +++ b/mihomo/Makefile @@ -5,8 +5,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=mihomo -PKG_VERSION:=1.19.28 -PKG_RELEASE:=30 +PKG_VERSION:=1.19.29 +PKG_RELEASE:=31 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/metacubex/mihomo/tar.gz/v$(PKG_VERSION)? diff --git a/openwrt-go-stun/Makefile b/openwrt-go-stun/Makefile index 06d67c4f..c991d5f1 100644 --- a/openwrt-go-stun/Makefile +++ b/openwrt-go-stun/Makefile @@ -8,11 +8,11 @@ PKG_NAME:=go-stun PKG_UPSTREAM_VERSION:=0.1.4 PKG_UPSTREAM_GITHASH:=d32c135f5e509d278e95ba9945849903f966a91f PKG_VERSION:=$(PKG_UPSTREAM_VERSION)~$(call version_abbrev,$(PKG_UPSTREAM_GITHASH)) -PKG_RELEASE:=5 +PKG_RELEASE:=6 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://github.com/ccding/go-stun.git -PKG_SOURCE_VERSION:=c6f6521d99a27227538c3bd8a7729f8b2f2a69dd +PKG_SOURCE_VERSION:=65c0568253e3a432f330b346d5ecad938d35d5aa PKG_MIRROR_HASH:=skip PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_UPSTREAM_VERSION)