diff --git a/luci-app-homeproxy/htdocs/luci-static/resources/view/homeproxy/client.js b/luci-app-homeproxy/htdocs/luci-static/resources/view/homeproxy/client.js index afe7b3e21..b9a2d296d 100644 --- a/luci-app-homeproxy/htdocs/luci-static/resources/view/homeproxy/client.js +++ b/luci-app-homeproxy/htdocs/luci-static/resources/view/homeproxy/client.js @@ -48,13 +48,13 @@ function getServiceStatus() { }); } -function renderStatus(isRunning) { - var spanTemp = '%s %s'; +function renderStatus(isRunning, version) { + var spanTemp = '%s (sing-box v%s) %s'; var renderHTML; if (isRunning) - renderHTML = spanTemp.format('green', _('HomeProxy'), _('RUNNING')); + renderHTML = spanTemp.format('green', _('HomeProxy'), version, _('RUNNING')); else - renderHTML = spanTemp.format('red', _('HomeProxy'), _('NOT RUNNING')); + renderHTML = spanTemp.format('red', _('HomeProxy'), version, _('NOT RUNNING')); return renderHTML; } @@ -125,7 +125,7 @@ return view.extend({ poll.add(function () { return L.resolveDefault(getServiceStatus()).then((res) => { var view = document.getElementById('service_status'); - view.innerHTML = renderStatus(res); + view.innerHTML = renderStatus(res, features.version); }); }); @@ -480,10 +480,30 @@ return view.extend({ so = ss.taboption('field_other', form.MultiValue, 'protocol', _('Protocol'), _('Sniffed protocol, see Sniff for details.')); + if (features.version.localeCompare('1.10.0', undefined, { numeric: true, sensitivity: 'base' }) >= 0) { + so.value('bittorrent', _('BitTorrent')); + so.value('dtls', _('DTLS')); + } so.value('http', _('HTTP')); - so.value('tls', _('TLS')); so.value('quic', _('QUIC')); + if (features.version.localeCompare('1.10.0', undefined, { numeric: true, sensitivity: 'base' }) >= 0) { + so.value('rdp', _('RDP')); + so.value('ssh', _('SSH')); + } so.value('stun', _('STUN')); + so.value('tls', _('TLS')); + + if (features.version.localeCompare('1.10.0', undefined, { numeric: true, sensitivity: 'base' }) >= 0) { + so = ss.taboption('field_other', form.Value, 'client', _('Client'), + _('Sniffed client type (QUIC client type or SSH client name).')); + so.value('chromium', _('Chromium / Cronet')); + so.value('firefox', _('Firefox / uquic firefox')); + so.value('quic-go', _('quic-go / uquic chrome')); + so.value('safari', _('Safari / Apple Network API')); + so.depends('protocol', 'quic'); + so.depends('protocol', 'ssh'); + so.modalonly = true; + } so = ss.taboption('field_other', form.ListValue, 'network', _('Network')); so.value('tcp', _('TCP')); @@ -557,6 +577,12 @@ return view.extend({ _('Match process path.')); so.modalonly = true; + if (features.version.localeCompare('1.10.0', undefined, { numeric: true, sensitivity: 'base' }) >= 0) { + so = ss.taboption('field_other', form.DynamicList, 'process_path_regex', _('Process path (regex)'), + _('Match process path using regular expression.')); + so.modalonly = true; + } + so = ss.taboption('field_other', form.DynamicList, 'user', _('User'), _('Match user name.')); so.modalonly = true; @@ -577,10 +603,9 @@ return view.extend({ } so.modalonly = true; - so = ss.taboption('field_other', form.Flag, 'rule_set_ipcidr_match_source', _('Match source IP via rule set'), + so = ss.taboption('field_other', form.Flag, 'rule_set_ip_cidr_match_source', _('Match source IP via rule set'), _('Make IP CIDR in rule set used to match the source IP.')); so.default = so.disabled; - so.rmempty = false; so.modalonly = true; so = ss.taboption('field_other', form.Flag, 'invert', _('Invert'), @@ -820,11 +845,18 @@ return view.extend({ so = ss.taboption('field_other', form.MultiValue, 'protocol', _('Protocol'), _('Sniffed protocol, see Sniff for details.')); + if (features.version.localeCompare('1.10.0', undefined, { numeric: true, sensitivity: 'base' }) >= 0) { + so.value('bittorrent', _('BitTorrent')); + so.value('dtls', _('DTLS')); + } so.value('http', _('HTTP')); - so.value('tls', _('TLS')); so.value('quic', _('QUIC')); - so.value('dns', _('DNS')); + if (features.version.localeCompare('1.10.0', undefined, { numeric: true, sensitivity: 'base' }) >= 0) { + so.value('rdp', _('RDP')); + so.value('ssh', _('SSH')); + } so.value('stun', _('STUN')); + so.value('tls', _('TLS')); so = ss.taboption('field_host', form.DynamicList, 'domain', _('Domain name'), _('Match full domain.')); @@ -891,6 +923,12 @@ return view.extend({ _('Match process path.')); so.modalonly = true; + if (features.version.localeCompare('1.10.0', undefined, { numeric: true, sensitivity: 'base' }) >= 0) { + so = ss.taboption('field_other', form.DynamicList, 'process_path_regex', _('Process path (regex)'), + _('Match process path using regular expression.')); + so.modalonly = true; + } + so = ss.taboption('field_other', form.DynamicList, 'user', _('User'), _('Match user name.')); so.modalonly = true; @@ -911,18 +949,25 @@ return view.extend({ } so.modalonly = true; - so = ss.taboption('field_other', form.Flag, 'rule_set_ipcidr_match_source', _('Rule set IP CIDR as source IP'), - _('Make ipcidr in rule sets match the source IP.')); + so = ss.taboption('field_other', form.Flag, 'rule_set_ip_cidr_match_source', _('Rule set IP CIDR as source IP'), + _('Make IP CIDR in rule sets match the source IP.')); so.default = so.disabled; so.modalonly = true; + if (features.version.localeCompare('1.10.0', undefined, { numeric: true, sensitivity: 'base' }) >= 0) { + so = ss.taboption('field_other', form.Flag, 'rule_set_ip_cidr_accept_empty', _('Accept empty query response.'), + _('Make IP CIDR in rule-sets accept empty query response.')); + so.default = so.disabled; + so.modalonly = true; + } + so = ss.taboption('field_other', form.Flag, 'invert', _('Invert'), _('Invert match result.')); so.default = so.disabled; so.modalonly = true; so = ss.taboption('field_other', form.MultiValue, 'outbound', _('Outbound'), - _('Match .outbounds[].server domains.')); + _('Match the server name of outbound.')); so.load = function(section_id) { delete this.keylist; delete this.vallist; diff --git a/luci-app-homeproxy/htdocs/luci-static/resources/view/homeproxy/node.js b/luci-app-homeproxy/htdocs/luci-static/resources/view/homeproxy/node.js index fcc110aa2..98691e331 100644 --- a/luci-app-homeproxy/htdocs/luci-static/resources/view/homeproxy/node.js +++ b/luci-app-homeproxy/htdocs/luci-static/resources/view/homeproxy/node.js @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: GPL-2.0-only * - * Copyright (C) 2022 ImmortalWrt.org + * Copyright (C) 2022-2024 ImmortalWrt.org */ 'use strict'; @@ -404,7 +404,7 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) { o.value('trojan', _('Trojan')); if (features.with_quic) o.value('tuic', _('Tuic')); - if (features.with_wireguard) + if (features.with_wireguard && features.with_gvisor) o.value('wireguard', _('WireGuard')); o.value('vless', _('VLESS')); o.value('vmess', _('VMess')); @@ -874,9 +874,8 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) { o = s.option(form.Value, 'wireguard_mtu', _('MTU')); o.datatype = 'range(0,9000)'; - o.default = '1408'; + o.placeholder = '1408'; o.depends('type', 'wireguard'); - o.rmempty = false; o.modalonly = true; /* Wireguard config end */ @@ -1062,11 +1061,13 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) { o.value('360'); o.value('android'); o.value('chrome'); - o.value('chrome_psk'); - o.value('chrome_psk_shuffle'); - o.value('chrome_padding_psk_shuffle'); - o.value('chrome_pq'); - o.value('chrome_pq_psk'); + if (features.version.localeCompare('1.10.0', undefined, { numeric: true, sensitivity: 'base' }) < 0) { + o.value('chrome_psk'); + o.value('chrome_psk_shuffle'); + o.value('chrome_padding_psk_shuffle'); + o.value('chrome_pq'); + o.value('chrome_pq_psk'); + } o.value('edge'); o.value('firefox'); o.value('ios'); diff --git a/luci-app-homeproxy/htdocs/luci-static/resources/view/homeproxy/server.js b/luci-app-homeproxy/htdocs/luci-static/resources/view/homeproxy/server.js index 9436cb83f..88a0756c9 100644 --- a/luci-app-homeproxy/htdocs/luci-static/resources/view/homeproxy/server.js +++ b/luci-app-homeproxy/htdocs/luci-static/resources/view/homeproxy/server.js @@ -31,13 +31,13 @@ function getServiceStatus() { }); } -function renderStatus(isRunning) { - var spanTemp = '%s %s'; +function renderStatus(isRunning, version) { + var spanTemp = '%s (sing-box v%s) %s'; var renderHTML; if (isRunning) - renderHTML = spanTemp.format('green', _('HomeProxy Server'), _('RUNNING')); + renderHTML = spanTemp.format('green', _('HomeProxy Server'), version, _('RUNNING')); else - renderHTML = spanTemp.format('red', _('HomeProxy Server'), _('NOT RUNNING')); + renderHTML = spanTemp.format('red', _('HomeProxy Server'), version, _('NOT RUNNING')); return renderHTML; } @@ -102,7 +102,7 @@ return view.extend({ poll.add(function () { return L.resolveDefault(getServiceStatus()).then((res) => { var view = document.getElementById('service_status'); - view.innerHTML = renderStatus(res); + view.innerHTML = renderStatus(res, features.version); }); }); @@ -265,6 +265,17 @@ return view.extend({ o = s.option(form.Value, 'hysteria_obfs_password', _('Obfuscate password')); o.depends('type', 'hysteria'); o.depends({'type': 'hysteria2', 'hysteria_obfs_type': /[\s\S]/}); + o.renderWidget = function() { + var node = form.Value.prototype.renderWidget.apply(this, arguments); + + (node.querySelector('.control-group') || node).appendChild(E('button', { + 'class': 'cbi-button cbi-button-apply', + 'title': _('Generate'), + 'click': ui.createHandlerFn(this, handleGenKey, this.option) + }, [ _('Generate') ])); + + return node; + } o.modalonly = true; o = s.option(form.Value, 'hysteria_recv_window_conn', _('QUIC stream receive window'), diff --git a/luci-app-homeproxy/root/etc/homeproxy/scripts/generate_client.uc b/luci-app-homeproxy/root/etc/homeproxy/scripts/generate_client.uc index 032443953..aafba8d91 100755 --- a/luci-app-homeproxy/root/etc/homeproxy/scripts/generate_client.uc +++ b/luci-app-homeproxy/root/etc/homeproxy/scripts/generate_client.uc @@ -2,7 +2,7 @@ /* * SPDX-License-Identifier: GPL-2.0-only * - * Copyright (C) 2023 ImmortalWrt.org + * Copyright (C) 2023-2024 ImmortalWrt.org */ 'use strict'; @@ -20,6 +20,8 @@ import { const ubus = connect(); +const features = ubus.call('luci.homeproxy', 'singbox_get_features') || {}; + /* UCI config start */ const uci = cursor(); @@ -205,9 +207,7 @@ function generate_outbound(node) { authenticated_length: node.vmess_authenticated_length ? (node.vmess_authenticated_length === '1') : null, packet_encoding: node.packet_encoding, /* WireGuard */ - system_interface: (node.type === 'wireguard') || null, gso: (node.wireguard_gso === '1') || null, - interface_name: (node.type === 'wireguard') ? 'wg-' + node['.name'] + '-out' : null, local_address: node.wireguard_local_address, private_key: node.wireguard_private_key, peer_public_key: node.wireguard_peer_public_key, @@ -454,9 +454,12 @@ if (!isEmpty(main_node)) { source_port_range: cfg.source_port_range, process_name: cfg.process_name, process_path: cfg.process_path, + process_path_regex: cfg.process_path_regex, user: cfg.user, rule_set: get_ruleset(cfg.rule_set), - rule_set_ipcidr_match_source: (cfg.rule_set_ipcidr_match_source === '1') || null, + /* rule_set_ipcidr_match_source is deprecated in sing-box 1.10.0 */ + rule_set_ipcidr_match_source: (features.version < '1.10.0' && cfg.rule_set_ip_cidr_match_source === '1') || null, + rule_set_ip_cidr_match_source: (features.version >= '1.10.0' && cfg.rule_set_ip_cidr_match_source === '1') || null, invert: (cfg.invert === '1') || null, outbound: get_outbound(cfg.outbound), server: get_resolver(cfg.server), @@ -525,8 +528,10 @@ if (match(proxy_mode, /tun/)) tag: 'tun-in', interface_name: tun_name, - inet4_address: tun_addr4, - inet6_address: (ipv6_support === '1') ? tun_addr6 : null, + /* inet4_address and inet6_address are deprecated in sing-box 1.10.0 */ + inet4_address: (features.version < '1.10.0') ? tun_addr4 : null, + inet6_address: (features.version < '1.10.0' && ipv6_support === '1') ? tun_addr6 : null, + address: (features.version >= '1.10.0') ? ((ipv6_support === '1') ? [tun_addr4, tun_addr6] : [tun_addr4]) : null, mtu: strToInt(tun_mtu), gso: (tun_gso === '1'), auto_route: false, @@ -639,9 +644,13 @@ if (!isEmpty(main_node)) { port_range: cfg.port_range, process_name: cfg.process_name, process_path: cfg.process_path, + process_path_regex: cfg.process_path_regex, user: cfg.user, rule_set: get_ruleset(cfg.rule_set), - rule_set_ipcidr_match_source: (cfg.rule_set_ipcidr_match_source === '1') || null, + /* rule_set_ipcidr_match_source is deprecated in sing-box 1.10.0 */ + rule_set_ipcidr_match_source: (features.version < '1.10.0' && cfg.rule_set_ip_cidr_match_source === '1') || null, + rule_set_ip_cidr_match_source: (features.version >= '1.10.0' && cfg.rule_set_ip_cidr_match_source === '1') || null, + rule_set_ip_cidr_accept_empty: (cfg.rule_set_ip_cidr_accept_empty === '1') || null, invert: (cfg.invert === '1') || null, outbound: get_outbound(cfg.outbound) }); diff --git a/luci-app-homeproxy/root/etc/homeproxy/scripts/update_resources.sh b/luci-app-homeproxy/root/etc/homeproxy/scripts/update_resources.sh index 576293d01..49d14606e 100755 --- a/luci-app-homeproxy/root/etc/homeproxy/scripts/update_resources.sh +++ b/luci-app-homeproxy/root/etc/homeproxy/scripts/update_resources.sh @@ -67,8 +67,7 @@ check_list_update() { log "[$(to_upper "$listtype")] Local version: $local_list_ver, latest version: $list_ver." fi - $wget "https://fastly.jsdelivr.net/gh/$listrepo@$list_sha/$listname" -O "$RUN_DIR/$listname" - if [ ! -s "$RUN_DIR/$listname" ]; then + if ! $wget "https://fastly.jsdelivr.net/gh/$listrepo@$list_sha/$listname" -O "$RUN_DIR/$listname" || [ ! -s "$RUN_DIR/$listname" ]; then rm -f "$RUN_DIR/$listname" log "[$(to_upper "$listtype")] Update failed." diff --git a/luci-app-homeproxy/root/etc/uci-defaults/luci-homeproxy-migration b/luci-app-homeproxy/root/etc/uci-defaults/luci-homeproxy-migration index e3a268eda..7b2d3f746 100644 --- a/luci-app-homeproxy/root/etc/uci-defaults/luci-homeproxy-migration +++ b/luci-app-homeproxy/root/etc/uci-defaults/luci-homeproxy-migration @@ -18,4 +18,6 @@ fi [ -z "$(uci -q changes "homeproxy")" ] || uci -q commit "homeproxy" +sed -i "s/rule_set_ipcidr_match_source/rule_set_ip_cidr_match_source/g" "/etc/config/homeproxy" + exit 0 diff --git a/luci-app-homeproxy/root/usr/share/rpcd/ucode/luci.homeproxy b/luci-app-homeproxy/root/usr/share/rpcd/ucode/luci.homeproxy index 34754c525..28b94a6db 100755 --- a/luci-app-homeproxy/root/usr/share/rpcd/ucode/luci.homeproxy +++ b/luci-app-homeproxy/root/usr/share/rpcd/ucode/luci.homeproxy @@ -2,7 +2,7 @@ /* * SPDX-License-Identifier: GPL-2.0-only * - * Copyright (C) 2023 ImmortalWrt.org + * Copyright (C) 2023-2024 ImmortalWrt.org */ 'use strict'; @@ -161,12 +161,13 @@ const methods = { const fd = popen('/usr/bin/sing-box version'); if (fd) { for (let line = fd.read('line'); length(line); line = fd.read('line')) { - let tags = match(trim(line), /Tags: (.*)/); - if (!tags) - continue; + if (match(trim(line), /^sing-box version (.*)/)) + features.version = match(trim(line), /^sing-box version (.*)/)[1]; - for (let i in split(tags[1], ',')) - features[i] = true; + let tags = match(trim(line), /^Tags: (.*)/); + if (tags) + for (let i in split(tags[1], ',')) + features[i] = true; } fd.close();