From f35837ff9f158267c8235af4ccc424b27024a457 Mon Sep 17 00:00:00 2001 From: xream Date: Mon, 3 Mar 2025 20:52:31 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=20AnyTLS=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 | 47 +++++++++++++++++++ backend/src/core/proxy-utils/producers/uri.js | 45 ++++++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/backend/package.json b/backend/package.json index 4aab1c8..dc0b42c 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "sub-store", - "version": "2.17.1", + "version": "2.17.2", "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 14b44a5..ad030ed 100644 --- a/backend/src/core/proxy-utils/parsers/index.js +++ b/backend/src/core/proxy-utils/parsers/index.js @@ -699,6 +699,52 @@ function URI_VLESS() { }; return { name, test, parse }; } +function URI_AnyTLS() { + const name = 'URI AnyTLS Parser'; + const test = (line) => { + return /^anytls:\/\//.test(line); + }; + const parse = (line) => { + line = line.split(/anytls:\/\//)[1]; + // eslint-disable-next-line no-unused-vars + let [__, password, server, port, addons = '', name] = + /^(.*?)@(.*?)(?::(\d+))?\/?(?:\?(.*?))?(?:#(.*?))?$/.exec(line); + password = decodeURIComponent(password); + port = parseInt(`${port}`, 10); + if (isNaN(port)) { + port = 443; + } + password = decodeURIComponent(password); + if (name != null) { + name = decodeURIComponent(name); + } + name = name ?? `AnyTLS ${server}:${port}`; + + const proxy = { + type: 'anytls', + name, + server, + port, + password, + }; + + for (const addon of addons.split('&')) { + let [key, value] = addon.split('='); + key = key.replace(/_/g, '-'); + value = decodeURIComponent(value); + if (['alpn'].includes(key)) { + proxy[key] = value ? value.split(',') : undefined; + } else if (['insecure'].includes(key)) { + proxy['skip-cert-verify'] = /(TRUE)|1/i.test(value); + } else { + proxy[key] = value; + } + } + + return proxy; + }; + return { name, test, parse }; +} function URI_Hysteria2() { const name = 'URI Hysteria2 Parser'; const test = (line) => { @@ -1544,6 +1590,7 @@ export default [ URI_Hysteria(), URI_Hysteria2(), URI_Trojan(), + URI_AnyTLS(), Clash_All(), Surge_Direct(), Surge_SSH(), diff --git a/backend/src/core/proxy-utils/producers/uri.js b/backend/src/core/proxy-utils/producers/uri.js index caf8ec3..5157fef 100644 --- a/backend/src/core/proxy-utils/producers/uri.js +++ b/backend/src/core/proxy-utils/producers/uri.js @@ -493,6 +493,7 @@ export default function URI_Producer() { 'password', 'server', 'port', + 'tls', ].includes(key) ) { const i = key.replace(/-/, '_'); @@ -542,6 +543,50 @@ export default function URI_Producer() { )}`; } break; + case 'anytls': + let anytlsParams = []; + Object.keys(proxy).forEach((key) => { + if ( + ![ + 'name', + 'type', + 'password', + 'server', + 'port', + 'tls', + ].includes(key) + ) { + const i = key.replace(/-/, '_'); + if (['alpn'].includes(key)) { + if (proxy[key]) { + anytlsParams.push( + `${i}=${encodeURIComponent( + Array.isArray(proxy[key]) + ? proxy[key][0] + : proxy[key], + )}`, + ); + } + } else if (['skip-cert-verify'].includes(key)) { + if (proxy[key]) { + anytlsParams.push(`insecure=1`); + } + } else if (proxy[key]) { + anytlsParams.push( + `${i.replace(/-/g, '_')}=${encodeURIComponent( + proxy[key], + )}`, + ); + } + } + }); + + result = `anytls://${encodeURIComponent(proxy.password)}@${ + proxy.server + }:${proxy.port}/?${anytlsParams.join('&')}#${encodeURIComponent( + proxy.name, + )}`; + break; case 'wireguard': let wireguardParams = [];