diff --git a/backend/package.json b/backend/package.json index baf4cc4..ec624cd 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "sub-store", - "version": "2.14.174", + "version": "2.14.175", "description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.", "main": "src/main.js", "scripts": { diff --git a/backend/src/core/proxy-utils/producers/loon.js b/backend/src/core/proxy-utils/producers/loon.js index e21476b..2d644fb 100644 --- a/backend/src/core/proxy-utils/producers/loon.js +++ b/backend/src/core/proxy-utils/producers/loon.js @@ -106,7 +106,19 @@ function trojan(proxy) { `,host=${proxy['ws-opts']?.headers?.Host}`, 'ws-opts.headers.Host', ); - } else { + } else if (proxy.network === 'http') { + result.append(`,transport=http`); + let httpPath = proxy['http-opts']?.path; + let httpHost = proxy['http-opts']?.headers?.Host; + result.appendIfPresent( + `,path=${Array.isArray(httpPath) ? httpPath[0] : httpPath}`, + 'http-opts.path', + ); + result.appendIfPresent( + `,host=${Array.isArray(httpHost) ? httpHost[0] : httpHost}`, + 'http-opts.headers.Host', + ); + } else if (!['tcp'].includes(proxy.network)) { throw new Error(`network ${proxy.network} is unsupported`); } } @@ -159,7 +171,7 @@ function vmess(proxy) { `,host=${Array.isArray(httpHost) ? httpHost[0] : httpHost}`, 'http-opts.headers.Host', ); - } else { + } else if (!['tcp'].includes(proxy.network)) { throw new Error(`network ${proxy.network} is unsupported`); } } else { @@ -195,7 +207,7 @@ function vmess(proxy) { function vless(proxy) { if (proxy['reality-opts']) { - throw new Error(`reality is unsupported`); + throw new Error(`VLESS REALITY is unsupported`); } const result = new Result(proxy); result.append( @@ -226,7 +238,7 @@ function vless(proxy) { `,host=${Array.isArray(httpHost) ? httpHost[0] : httpHost}`, 'http-opts.headers.Host', ); - } else { + } else if (!['tcp'].includes(proxy.network)) { throw new Error(`network ${proxy.network} is unsupported`); } } else { diff --git a/backend/src/core/proxy-utils/producers/qx.js b/backend/src/core/proxy-utils/producers/qx.js index 614f67a..1adb55b 100644 --- a/backend/src/core/proxy-utils/producers/qx.js +++ b/backend/src/core/proxy-utils/producers/qx.js @@ -3,7 +3,7 @@ import { isPresent, Result } from './utils'; const targetPlatform = 'QX'; export default function QX_Producer() { - const produce = (proxy) => { + const produce = (proxy, type, opts = {}) => { switch (proxy.type) { case 'ss': return shadowsocks(proxy); @@ -17,6 +17,14 @@ export default function QX_Producer() { return http(proxy); case 'socks5': return socks5(proxy); + case 'vless': + if (opts['include-unsupported-proxy']) { + return vless(proxy); + } else { + throw new Error( + `Platform ${targetPlatform}(App Store Release) does not support proxy type: ${proxy.type}`, + ); + } } throw new Error( `Platform ${targetPlatform} does not support proxy type: ${proxy.type}`, @@ -325,6 +333,105 @@ function vmess(proxy) { return result.toString(); } +function vless(proxy) { + if (typeof proxy.flow !== 'undefined' || proxy['reality-opts']) { + throw new Error(`VLESS XTLS/REALITY is not supported`); + } + + const result = new Result(proxy); + const append = result.append.bind(result); + const appendIfPresent = result.appendIfPresent.bind(result); + + append(`vless=${proxy.server}:${proxy.port}`); + + // The method field for vless should be none. + let cipher = 'none'; + // if (proxy.cipher === 'auto') { + // cipher = 'chacha20-ietf-poly1305'; + // } else { + // cipher = proxy.cipher; + // } + append(`,method=${cipher}`); + + append(`,password=${proxy.uuid}`); + + // obfs + if (needTls(proxy)) { + proxy.tls = true; + } + if (isPresent(proxy, 'network')) { + if (proxy.network === 'ws') { + if (proxy.tls) append(`,obfs=wss`); + else append(`,obfs=ws`); + } else if (proxy.network === 'http') { + append(`,obfs=http`); + } else if (!['tcp'].includes(proxy.network)) { + throw new Error(`network ${proxy.network} is unsupported`); + } + let transportPath = proxy[`${proxy.network}-opts`]?.path; + let transportHost = proxy[`${proxy.network}-opts`]?.headers?.Host; + appendIfPresent( + `,obfs-uri=${ + Array.isArray(transportPath) ? transportPath[0] : transportPath + }`, + `${proxy.network}-opts.path`, + ); + appendIfPresent( + `,obfs-host=${ + Array.isArray(transportHost) ? transportHost[0] : transportHost + }`, + `${proxy.network}-opts.headers.Host`, + ); + } else { + // over-tls + if (proxy.tls) append(`,obfs=over-tls`); + } + + if (needTls(proxy)) { + appendIfPresent( + `,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`, + 'tls-pubkey-sha256', + ); + appendIfPresent(`,tls-alpn=${proxy['tls-alpn']}`, 'tls-alpn'); + appendIfPresent( + `,tls-no-session-ticket=${proxy['tls-no-session-ticket']}`, + 'tls-no-session-ticket', + ); + appendIfPresent( + `,tls-no-session-reuse=${proxy['tls-no-session-reuse']}`, + 'tls-no-session-reuse', + ); + // tls fingerprint + appendIfPresent( + `,tls-cert-sha256=${proxy['tls-fingerprint']}`, + 'tls-fingerprint', + ); + + // tls verification + appendIfPresent( + `,tls-verification=${!proxy['skip-cert-verify']}`, + 'skip-cert-verify', + ); + appendIfPresent(`,tls-host=${proxy.sni}`, 'sni'); + } + + // tfo + appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo'); + + // udp + appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp'); + + // server_check_url + result.appendIfPresent( + `,server_check_url=${proxy['test-url']}`, + 'test-url', + ); + + // tag + append(`,tag=${proxy.name}`); + + return result.toString(); +} function http(proxy) { const result = new Result(proxy);