diff --git a/backend/package.json b/backend/package.json index 6889985..90b0403 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "sub-store", - "version": "2.19.5", + "version": "2.19.6", "description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.", "main": "src/main.js", "scripts": { diff --git a/backend/src/core/proxy-utils/parsers/peggy/loon.js b/backend/src/core/proxy-utils/parsers/peggy/loon.js index 9615037..5a1c979 100644 --- a/backend/src/core/proxy-utils/parsers/peggy/loon.js +++ b/backend/src/core/proxy-utils/parsers/peggy/loon.js @@ -60,7 +60,7 @@ vmess = tag equals "vmess"i address method uuid (transport/transport_host/transp proxy.alterId = proxy.alterId || 0; handleTransport(); } -vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/others)* { +vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/flow/public_key/short_id/others)* { proxy.type = "vless"; handleTransport(); } @@ -180,6 +180,10 @@ tls_verification = comma "skip-cert-verify" equals flag:bool { proxy["skip-cert- tls_cert_sha256 = comma "tls-cert-sha256" equals match:[^,]+ { proxy["tls-fingerprint"] = match.join("").replace(/^"(.*)"$/, '$1'); } tls_pubkey_sha256 = comma "tls-pubkey-sha256" equals match:[^,]+ { proxy["tls-pubkey-sha256"] = match.join("").replace(/^"(.*)"$/, '$1'); } +flow = comma "flow" equals match:[^,]+ { proxy["flow"] = match.join("").replace(/^"(.*)"$/, '$1'); } +public_key = comma "public-key" equals match:[^,]+ { proxy["reality-opts"] = proxy["reality-opts"] || {}; proxy["reality-opts"]["public-key"] = match.join("").replace(/^"(.*)"$/, '$1'); } +short_id = comma "short-id" equals match:[^,]+ { proxy["reality-opts"] = proxy["reality-opts"] || {}; proxy["reality-opts"]["short-id"] = match.join("").replace(/^"(.*)"$/, '$1'); } + fast_open = comma "fast-open" equals flag:bool { proxy.tfo = flag; } udp_relay = comma "udp" equals flag:bool { proxy.udp = flag; } ip_mode = comma "ip-mode" equals match:[^,]+ { proxy["ip-version"] = match.join(""); } diff --git a/backend/src/core/proxy-utils/parsers/peggy/loon.peg b/backend/src/core/proxy-utils/parsers/peggy/loon.peg index 60f3623..66e93c0 100644 --- a/backend/src/core/proxy-utils/parsers/peggy/loon.peg +++ b/backend/src/core/proxy-utils/parsers/peggy/loon.peg @@ -58,7 +58,7 @@ vmess = tag equals "vmess"i address method uuid (transport/transport_host/transp proxy.alterId = proxy.alterId || 0; handleTransport(); } -vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/others)* { +vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/flow/public_key/short_id/others)* { proxy.type = "vless"; handleTransport(); } @@ -178,6 +178,10 @@ tls_verification = comma "skip-cert-verify" equals flag:bool { proxy["skip-cert- tls_cert_sha256 = comma "tls-cert-sha256" equals match:[^,]+ { proxy["tls-fingerprint"] = match.join("").replace(/^"(.*)"$/, '$1'); } tls_pubkey_sha256 = comma "tls-pubkey-sha256" equals match:[^,]+ { proxy["tls-pubkey-sha256"] = match.join("").replace(/^"(.*)"$/, '$1'); } +flow = comma "flow" equals match:[^,]+ { proxy["flow"] = match.join("").replace(/^"(.*)"$/, '$1'); } +public_key = comma "public-key" equals match:[^,]+ { proxy["reality-opts"] = proxy["reality-opts"] || {}; proxy["reality-opts"]["public-key"] = match.join("").replace(/^"(.*)"$/, '$1'); } +short_id = comma "short-id" equals match:[^,]+ { proxy["reality-opts"] = proxy["reality-opts"] || {}; proxy["reality-opts"]["short-id"] = match.join("").replace(/^"(.*)"$/, '$1'); } + fast_open = comma "fast-open" equals flag:bool { proxy.tfo = flag; } udp_relay = comma "udp" equals flag:bool { proxy.udp = flag; } ip_mode = comma "ip-mode" equals match:[^,]+ { proxy["ip-version"] = match.join(""); } diff --git a/backend/src/core/proxy-utils/producers/loon.js b/backend/src/core/proxy-utils/producers/loon.js index 363a36c..7126f85 100644 --- a/backend/src/core/proxy-utils/producers/loon.js +++ b/backend/src/core/proxy-utils/producers/loon.js @@ -23,7 +23,7 @@ export default function Loon_Producer() { case 'vmess': return vmess(proxy); case 'vless': - return vless(proxy); + return vless(proxy, opts['include-unsupported-proxy']); case 'http': return http(proxy); case 'socks5': @@ -347,10 +347,26 @@ function vmess(proxy) { return result.toString(); } -function vless(proxy) { - if (typeof proxy.flow !== 'undefined' || proxy['reality-opts']) { +function vless(proxy, includeUnsupportedProxy) { + if ( + !includeUnsupportedProxy && + (typeof proxy.flow !== 'undefined' || proxy['reality-opts']) + ) { throw new Error(`VLESS XTLS/REALITY is not supported`); } + let isReality = false; + if (includeUnsupportedProxy) { + if ( + proxy['reality-opts'] && + ['xtls-rprx-vision'].includes(proxy.flow) + ) { + isReality = true; + } else if (proxy['reality-opts'] || proxy.flow) { + throw new Error( + `VLESS XTLS/REALITY with flow(${proxy.flow}) is not supported`, + ); + } + } const result = new Result(proxy); result.append( `${proxy.name}=vless,${proxy.server},${proxy.port},"${proxy.uuid}"`, @@ -399,7 +415,20 @@ function vless(proxy) { ); // sni - result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni'); + if (isReality) { + result.appendIfPresent(`,sni=${proxy.sni}`, 'sni'); + result.appendIfPresent( + `,public-key="${proxy['reality-opts']['public-key']}"`, + 'reality-opts.public-key', + ); + result.appendIfPresent( + `,short-id=${proxy['reality-opts']['short-id']}`, + 'reality-opts.short-id', + ); + } else { + result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni'); + } + result.appendIfPresent( `,tls-cert-sha256=${proxy['tls-fingerprint']}`, 'tls-fingerprint', diff --git a/backend/src/utils/user-agent.js b/backend/src/utils/user-agent.js index 0349bae..b8bbbd5 100644 --- a/backend/src/utils/user-agent.js +++ b/backend/src/utils/user-agent.js @@ -66,10 +66,12 @@ export function shouldIncludeUnsupportedProxy(platform, ua) { UA: ua, ua: ua.toLowerCase(), }); - if (!['Stash', 'Egern'].includes(target)) { + if (!['Stash', 'Egern', 'Loon'].includes(target)) { return false; } - const version = coerce(ua).version; + const coerceVersion = coerce(ua); + $.log(JSON.stringify(coerceVersion, null, 2)); + const { version } = coerceVersion; if ( platform === 'Stash' && target === 'Stash' && @@ -84,6 +86,14 @@ export function shouldIncludeUnsupportedProxy(platform, ua) { ) { return true; } + // Loon 的 UA 不规范, version 取出来是 build + if ( + platform === 'Loon' && + target === 'Loon' && + gte(version, '838.0.0') + ) { + return true; + } } catch (e) { $.error(`获取版本号失败: ${e}`); }