feat: Loon 输入输出支持 VLESS XTLS/REALITY, VMess REALITY. 需 includeUnsupportedProxy 或 build >= 842 自动开启)

This commit is contained in:
xream 2025-04-01 18:22:28 +08:00
parent 39829fa97a
commit 73e5d53f48
No known key found for this signature in database
GPG Key ID: 1D2C5225471789F9
6 changed files with 62 additions and 39 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "sub-store", "name": "sub-store",
"version": "2.19.13", "version": "2.19.14",
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.", "description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
"main": "src/main.js", "main": "src/main.js",
"scripts": { "scripts": {

View File

@ -54,7 +54,7 @@ shadowsocks = tag equals "shadowsocks"i address method password (obfs_typev obfs
$set(proxy, "plugin-opts.path", obfs.path); $set(proxy, "plugin-opts.path", obfs.path);
} }
} }
vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/vmess_alterId/fast_open/udp_relay/ip_mode/others)* { vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/vmess_alterId/fast_open/udp_relay/ip_mode/public_key/short_id/others)* {
proxy.type = "vmess"; proxy.type = "vmess";
proxy.cipher = proxy.cipher || "none"; proxy.cipher = proxy.cipher || "none";
proxy.alterId = proxy.alterId || 0; proxy.alterId = proxy.alterId || 0;

View File

@ -52,7 +52,7 @@ shadowsocks = tag equals "shadowsocks"i address method password (obfs_typev obfs
$set(proxy, "plugin-opts.path", obfs.path); $set(proxy, "plugin-opts.path", obfs.path);
} }
} }
vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/vmess_alterId/fast_open/udp_relay/ip_mode/others)* { vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/vmess_alterId/fast_open/udp_relay/ip_mode/public_key/short_id/others)* {
proxy.type = "vmess"; proxy.type = "vmess";
proxy.cipher = proxy.cipher || "none"; proxy.cipher = proxy.cipher || "none";
proxy.alterId = proxy.alterId || 0; proxy.alterId = proxy.alterId || 0;

View File

@ -21,7 +21,7 @@ export default function Loon_Producer() {
case 'trojan': case 'trojan':
return trojan(proxy); return trojan(proxy);
case 'vmess': case 'vmess':
return vmess(proxy); return vmess(proxy, opts['include-unsupported-proxy']);
case 'vless': case 'vless':
return vless(proxy, opts['include-unsupported-proxy']); return vless(proxy, opts['include-unsupported-proxy']);
case 'http': case 'http':
@ -269,7 +269,17 @@ function trojan(proxy) {
return result.toString(); return result.toString();
} }
function vmess(proxy) { function vmess(proxy, includeUnsupportedProxy) {
if (!includeUnsupportedProxy && proxy['reality-opts']) {
throw new Error(`VMess REALITY is not supported`);
}
let isReality = false;
if (includeUnsupportedProxy) {
if (proxy['reality-opts']) {
isReality = true;
}
}
const result = new Result(proxy); const result = new Result(proxy);
result.append( result.append(
`${proxy.name}=vmess,${proxy.server},${proxy.port},${proxy.cipher},"${proxy.uuid}"`, `${proxy.name}=vmess,${proxy.server},${proxy.port},${proxy.cipher},"${proxy.uuid}"`,
@ -317,16 +327,28 @@ function vmess(proxy) {
'skip-cert-verify', 'skip-cert-verify',
); );
// sni if (isReality) {
result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni'); result.appendIfPresent(`,sni=${proxy.sni}`, 'sni');
result.appendIfPresent( result.appendIfPresent(
`,tls-cert-sha256=${proxy['tls-fingerprint']}`, `,public-key="${proxy['reality-opts']['public-key']}"`,
'tls-fingerprint', 'reality-opts.public-key',
); );
result.appendIfPresent( result.appendIfPresent(
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`, `,short-id=${proxy['reality-opts']['short-id']}`,
'tls-pubkey-sha256', 'reality-opts.short-id',
); );
} else {
// sni
result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni');
result.appendIfPresent(
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
'tls-fingerprint',
);
result.appendIfPresent(
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
'tls-pubkey-sha256',
);
}
// AEAD // AEAD
if (isPresent(proxy, 'aead')) { if (isPresent(proxy, 'aead')) {
@ -354,19 +376,19 @@ function vless(proxy, includeUnsupportedProxy) {
) { ) {
throw new Error(`VLESS XTLS/REALITY is not supported`); throw new Error(`VLESS XTLS/REALITY is not supported`);
} }
let isXtls = false;
let isReality = false; let isReality = false;
if (includeUnsupportedProxy) { if (includeUnsupportedProxy) {
if ( if (proxy['reality-opts']) {
proxy['reality-opts'] &&
['xtls-rprx-vision'].includes(proxy.flow)
) {
isReality = true; isReality = true;
} else if (proxy['reality-opts']) { }
throw new Error(
`VLESS REALITY with flow(${proxy.flow}) is not supported`, if (typeof proxy.flow !== 'undefined') {
); if (['xtls-rprx-vision'].includes(proxy.flow)) {
} else if (proxy.flow) { isXtls = true;
throw new Error(`VLESS XTLS is not supported`); } else {
throw new Error(`VLESS flow(${proxy.flow}) is not supported`);
}
} }
} }
const result = new Result(proxy); const result = new Result(proxy);
@ -416,9 +438,10 @@ function vless(proxy, includeUnsupportedProxy) {
'skip-cert-verify', 'skip-cert-verify',
); );
// sni if (isXtls) {
if (isReality) {
result.appendIfPresent(`,flow=${proxy.flow}`, 'flow'); result.appendIfPresent(`,flow=${proxy.flow}`, 'flow');
}
if (isReality) {
result.appendIfPresent(`,sni=${proxy.sni}`, 'sni'); result.appendIfPresent(`,sni=${proxy.sni}`, 'sni');
result.appendIfPresent( result.appendIfPresent(
`,public-key="${proxy['reality-opts']['public-key']}"`, `,public-key="${proxy['reality-opts']['public-key']}"`,
@ -429,18 +452,18 @@ function vless(proxy, includeUnsupportedProxy) {
'reality-opts.short-id', 'reality-opts.short-id',
); );
} else { } else {
// sni
result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni'); result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni');
result.appendIfPresent(
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
'tls-fingerprint',
);
result.appendIfPresent(
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
'tls-pubkey-sha256',
);
} }
result.appendIfPresent(
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
'tls-fingerprint',
);
result.appendIfPresent(
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
'tls-pubkey-sha256',
);
// tfo // tfo
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo'); result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');

View File

@ -44,7 +44,7 @@ let resourceUrl = typeof $resourceUrl !== 'undefined' ? $resourceUrl : '';
let proxies = ProxyUtils.parse(resource); let proxies = ProxyUtils.parse(resource);
result = ProxyUtils.produce(proxies, 'Loon', undefined, { result = ProxyUtils.produce(proxies, 'Loon', undefined, {
'include-unsupported-proxy': 'include-unsupported-proxy':
arg?.includeUnsupportedProxy || build >= 838, arg?.includeUnsupportedProxy || build >= 842,
}); });
} catch (e) { } catch (e) {
console.log('解析器: 使用 resource 出现错误'); console.log('解析器: 使用 resource 出现错误');
@ -67,7 +67,7 @@ let resourceUrl = typeof $resourceUrl !== 'undefined' ? $resourceUrl : '';
let proxies = ProxyUtils.parse(raw); let proxies = ProxyUtils.parse(raw);
result = ProxyUtils.produce(proxies, 'Loon', undefined, { result = ProxyUtils.produce(proxies, 'Loon', undefined, {
'include-unsupported-proxy': 'include-unsupported-proxy':
arg?.includeUnsupportedProxy || build >= 838, arg?.includeUnsupportedProxy || build >= 842,
}); });
} catch (e) { } catch (e) {
console.log(e.message ?? e); console.log(e.message ?? e);

View File

@ -90,7 +90,7 @@ export function shouldIncludeUnsupportedProxy(platform, ua) {
if ( if (
platform === 'Loon' && platform === 'Loon' &&
target === 'Loon' && target === 'Loon' &&
gte(version, '838.0.0') gte(version, '842.0.0')
) { ) {
return true; return true;
} }