From 5d3fc499ce5ad472a12a53c2419830781b86ff72 Mon Sep 17 00:00:00 2001 From: xream Date: Sat, 3 Feb 2024 16:17:26 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=20TUIC=20v5=20URI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/package.json | 2 +- backend/src/core/proxy-utils/parsers/index.js | 50 ++++++++++++++++ .../src/core/proxy-utils/processors/index.js | 1 + .../core/proxy-utils/producers/sing-box.js | 29 ++++++++++ backend/src/core/proxy-utils/producers/uri.js | 58 +++++++++++++++++++ 5 files changed, 139 insertions(+), 1 deletion(-) diff --git a/backend/package.json b/backend/package.json index e36526d..067e090 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "sub-store", - "version": "2.14.202", + "version": "2.14.203", "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/index.js b/backend/src/core/proxy-utils/parsers/index.js index 45ca22e..9b5317d 100644 --- a/backend/src/core/proxy-utils/parsers/index.js +++ b/backend/src/core/proxy-utils/parsers/index.js @@ -545,6 +545,55 @@ function URI_Hysteria2() { }; return { name, test, parse }; } +function URI_TUIC() { + const name = 'URI TUIC Parser'; + const test = (line) => { + return /^tuic:\/\//.test(line); + }; + const parse = (line) => { + line = line.split(/tuic:\/\//)[1]; + console.log(line); + // eslint-disable-next-line no-unused-vars + let [__, uuid, password, server, ___, port, ____, addons = '', name] = + /^(.*?):(.*?)@(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line); + port = parseInt(`${port}`, 10); + if (isNaN(port)) { + port = 443; + } + password = decodeURIComponent(password); + if (name != null) { + name = decodeURIComponent(name); + } + name = name ?? `TUIC ${server}:${port}`; + + const proxy = { + type: 'tuic', + name, + server, + port, + password, + uuid, + }; + + for (const addon of addons.split('&')) { + let [key, value] = addon.split('='); + key = key.replace(/_/, '-'); + value = decodeURIComponent(value); + if (['alpn'].includes(key)) { + proxy[key] = value ? value.split(',') : undefined; + } else if (['allow-insecure'].includes(key)) { + proxy['skip-cert-verify'] = /(TRUE)|1/i.test(value); + } else if (['disable-sni', 'reduce-rtt'].includes(key)) { + proxy[key] = /(TRUE)|1/i.test(value); + } else { + proxy[key] = value; + } + } + + return proxy; + }; + return { name, test, parse }; +} // Trojan URI format function URI_Trojan() { @@ -1051,6 +1100,7 @@ export default [ URI_SSR(), URI_VMess(), URI_VLESS(), + URI_TUIC(), URI_Hysteria2(), URI_Trojan(), Clash_All(), diff --git a/backend/src/core/proxy-utils/processors/index.js b/backend/src/core/proxy-utils/processors/index.js index ed115b7..24a3de2 100644 --- a/backend/src/core/proxy-utils/processors/index.js +++ b/backend/src/core/proxy-utils/processors/index.js @@ -93,6 +93,7 @@ function QuickSettingOperator(args) { return proxies.map((proxy) => { proxy.udp = get(args.udp, proxy.udp); proxy.tfo = get(args.tfo, proxy.tfo); + proxy['fast-open'] = get(args.tfo, proxy['fast-open']); proxy['skip-cert-verify'] = get( args.scert, proxy['skip-cert-verify'], diff --git a/backend/src/core/proxy-utils/producers/sing-box.js b/backend/src/core/proxy-utils/producers/sing-box.js index 0214a3d..cc1247c 100644 --- a/backend/src/core/proxy-utils/producers/sing-box.js +++ b/backend/src/core/proxy-utils/producers/sing-box.js @@ -637,6 +637,35 @@ export default function singbox_Producer() { } break; case 'ss': + // if (!proxy.cipher) { + // proxy.cipher = 'none'; + // } + // if ( + // ![ + // '2022-blake3-aes-128-gcm', + // '2022-blake3-aes-256-gcm', + // '2022-blake3-chacha20-poly1305', + // 'aes-128-cfb', + // 'aes-128-ctr', + // 'aes-128-gcm', + // 'aes-192-cfb', + // 'aes-192-ctr', + // 'aes-192-gcm', + // 'aes-256-cfb', + // 'aes-256-ctr', + // 'aes-256-gcm', + // 'chacha20-ietf', + // 'chacha20-ietf-poly1305', + // 'none', + // 'rc4-md5', + // 'xchacha20', + // 'xchacha20-ietf-poly1305', + // ].includes(proxy.cipher) + // ) { + // throw new Error( + // `cipher ${proxy.cipher} is not supported`, + // ); + // } if (proxy.plugin === 'shadow-tls') { const { ssPart, stPart } = shadowTLSParser(proxy); diff --git a/backend/src/core/proxy-utils/producers/uri.js b/backend/src/core/proxy-utils/producers/uri.js index b304504..c6511e1 100644 --- a/backend/src/core/proxy-utils/producers/uri.js +++ b/backend/src/core/proxy-utils/producers/uri.js @@ -285,6 +285,64 @@ export default function URI_Producer() { '&', )}#${encodeURIComponent(proxy.name)}`; break; + case 'tuic': + if (!proxy.token || proxy.token.length === 0) { + let tuicParams = []; + Object.keys(proxy).forEach((key) => { + if ( + ![ + 'name', + 'type', + 'uuid', + 'password', + 'server', + 'port', + ].includes(key) + ) { + const i = key.replace(/-/, '_'); + if (['alpn'].includes(key)) { + if (proxy[key]) { + tuicParams.push( + `${i}=${encodeURIComponent( + Array.isArray(proxy[key]) + ? proxy[key][0] + : proxy[key], + )}`, + ); + } + } else if (['skip-cert-verify'].includes(key)) { + if (proxy[key]) { + tuicParams.push(`allow_insecure=1`); + } + } else if (['tfo', 'fast-open'].includes(key)) { + if ( + proxy[key] && + !tuicParams.includes('fast_open=1') + ) { + tuicParams.push(`fast_open=1`); + } + } else if ( + ['disable-sni', 'reduce-rtt'].includes(key) && + proxy[key] + ) { + tuicParams.push(`${i}=1`); + } else if (proxy[key]) { + tuicParams.push( + `${i}=${encodeURIComponent(proxy[key])}`, + ); + } + } + }); + + result = `tuic://${encodeURIComponent( + proxy.uuid, + )}:${encodeURIComponent(proxy.password)}@${proxy.server}:${ + proxy.port + }?${tuicParams.join('&')}#${encodeURIComponent( + proxy.name, + )}`; + break; + } } return result; };