mirror of
https://git.mirrors.martin98.com/https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 02:59:01 +08:00
feat: Surge Hysteria2 与 TUIC 协议支持端口跳跃; Hysteria2 URI 的端口部分支持 端口跳跃 的「多端口地址格式」
This commit is contained in:
parent
562d349629
commit
59fe16a7b0
@ -10,10 +10,10 @@
|
||||
Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.
|
||||
</p>
|
||||
|
||||
[](https://github.com/sub-store-org/Sub-Store/actions/workflows/main.yml)     
|
||||
[](https://github.com/sub-store-org/Sub-Store/actions/workflows/main.yml)     
|
||||
<a href="https://trendshift.io/repositories/4572" target="_blank"><img src="https://trendshift.io/api/badge/repositories/4572" alt="sub-store-org%2FSub-Store | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
[](https://www.buymeacoffee.com/PengYM)
|
||||
|
||||
|
||||
Core functionalities:
|
||||
|
||||
1. Conversion among various formats.
|
||||
@ -21,7 +21,7 @@ Core functionalities:
|
||||
3. Collect multiple subscriptions in one URL.
|
||||
|
||||
> The following descriptions of features may not be updated in real-time. Please refer to the actual available features for accurate information.
|
||||
|
||||
|
||||
## 1. Subscription Conversion
|
||||
|
||||
### Supported Input Formats
|
||||
@ -98,7 +98,7 @@ or
|
||||
esbuild(experimental)
|
||||
|
||||
```
|
||||
pnpm run --parallel "/^dev:.*/"
|
||||
SUB_STORE_BACKEND_API_PORT=3000 pnpm run --parallel "/^dev:.*/"
|
||||
```
|
||||
|
||||
## LICENSE
|
||||
@ -111,7 +111,6 @@ This project is under the GPL V3 LICENSE.
|
||||
|
||||
[](https://star-history.com/#sub-store-org/sub-store&Date)
|
||||
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
- Special thanks to @KOP-XIAO for his awesome resource-parser. Please give a [star](https://github.com/KOP-XIAO/QuantumultX) for his great work!
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sub-store",
|
||||
"version": "2.14.368",
|
||||
"version": "2.14.370",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
|
@ -424,8 +424,12 @@ function lastParse(proxy) {
|
||||
proxy[`${proxy.network}-opts`].path = [transportPath];
|
||||
}
|
||||
}
|
||||
if (['hysteria', 'hysteria2'].includes(proxy.type) && !proxy.ports) {
|
||||
delete proxy.ports;
|
||||
if (['hysteria', 'hysteria2'].includes(proxy.type)) {
|
||||
if (proxy.ports) {
|
||||
proxy.ports = proxy.ports.replace(/\//g, ',');
|
||||
} else {
|
||||
delete proxy.ports;
|
||||
}
|
||||
}
|
||||
if (
|
||||
['hysteria2'].includes(proxy.type) &&
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
isPresent,
|
||||
isNotBlank,
|
||||
getIfPresent,
|
||||
getRandomPort,
|
||||
} from '@/utils';
|
||||
import getSurgeParser from './peggy/surge';
|
||||
import getLoonParser from './peggy/loon';
|
||||
@ -13,6 +14,19 @@ import getTrojanURIParser from './peggy/trojan-uri';
|
||||
|
||||
import { Base64 } from 'js-base64';
|
||||
|
||||
function surge_port_hopping(raw) {
|
||||
const [parts, port_hopping] =
|
||||
raw.match(
|
||||
/,\s*?port-hopping\s*?=\s*?["']?\s*?((\d+(-\d+)?)([,;]\d+(-\d+)?)*)\s*?["']?\s*?/,
|
||||
) || [];
|
||||
return {
|
||||
port_hopping: port_hopping
|
||||
? port_hopping.replace(/;/g, ',')
|
||||
: undefined,
|
||||
line: parts ? raw.replace(parts, '') : raw,
|
||||
};
|
||||
}
|
||||
|
||||
// Parse SS URI format (only supports new SIP002, legacy format is depreciated).
|
||||
// reference: https://github.com/shadowsocks/shadowsocks-org/wiki/SIP002-URI-Scheme
|
||||
function URI_SS() {
|
||||
@ -545,13 +559,42 @@ function URI_Hysteria2() {
|
||||
};
|
||||
const parse = (line) => {
|
||||
line = line.split(/(hysteria2|hy2):\/\//)[2];
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
let [__, password, server, ___, port, ____, addons = '', name] =
|
||||
/^(.*?)@(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line);
|
||||
port = parseInt(`${port}`, 10);
|
||||
if (isNaN(port)) {
|
||||
// 端口跳跃有两种写法:
|
||||
// 1. 服务器的地址和可选端口。如果省略端口,则默认为 443。
|
||||
// 端口部分支持 端口跳跃 的「多端口地址格式」。
|
||||
// https://hysteria.network/zh/docs/advanced/Port-Hopping
|
||||
// 2. 参数 mport
|
||||
let ports;
|
||||
/* eslint-disable no-unused-vars */
|
||||
let [
|
||||
__,
|
||||
password,
|
||||
server,
|
||||
___,
|
||||
port,
|
||||
____,
|
||||
_____,
|
||||
______,
|
||||
_______,
|
||||
________,
|
||||
addons = '',
|
||||
name,
|
||||
] = /^(.*?)@(.*?)(:((\d+(-\d+)?)([,;]\d+(-\d+)?)*))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(
|
||||
line,
|
||||
);
|
||||
/* eslint-enable no-unused-vars */
|
||||
if (/^\d+$/.test(port)) {
|
||||
port = parseInt(`${port}`, 10);
|
||||
if (isNaN(port)) {
|
||||
port = 443;
|
||||
}
|
||||
} else if (port) {
|
||||
ports = port;
|
||||
port = getRandomPort(ports);
|
||||
} else {
|
||||
port = 443;
|
||||
}
|
||||
|
||||
password = decodeURIComponent(password);
|
||||
if (name != null) {
|
||||
name = decodeURIComponent(name);
|
||||
@ -563,6 +606,7 @@ function URI_Hysteria2() {
|
||||
name,
|
||||
server,
|
||||
port,
|
||||
ports,
|
||||
password,
|
||||
};
|
||||
|
||||
@ -1295,7 +1339,12 @@ function Surge_Tuic() {
|
||||
const test = (line) => {
|
||||
return /^.*=\s*tuic(-v5)?/.test(line.split(',')[0]);
|
||||
};
|
||||
const parse = (line) => getSurgeParser().parse(line);
|
||||
const parse = (raw) => {
|
||||
const { port_hopping, line } = surge_port_hopping(raw);
|
||||
const proxy = getSurgeParser().parse(line);
|
||||
proxy['ports'] = port_hopping;
|
||||
return proxy;
|
||||
};
|
||||
return { name, test, parse };
|
||||
}
|
||||
function Surge_WireGuard() {
|
||||
@ -1312,7 +1361,12 @@ function Surge_Hysteria2() {
|
||||
const test = (line) => {
|
||||
return /^.*=\s*hysteria2/.test(line.split(',')[0]);
|
||||
};
|
||||
const parse = (line) => getSurgeParser().parse(line);
|
||||
const parse = (raw) => {
|
||||
const { port_hopping, line } = surge_port_hopping(raw);
|
||||
const proxy = getSurgeParser().parse(line);
|
||||
proxy['ports'] = port_hopping;
|
||||
return proxy;
|
||||
};
|
||||
return { name, test, parse };
|
||||
}
|
||||
|
||||
|
@ -91,11 +91,11 @@ snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_
|
||||
}
|
||||
handleShadowTLS();
|
||||
}
|
||||
tuic = tag equals "tuic" address (alpn/token/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_fingerprint/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
tuic = tag equals "tuic" address (alpn/token/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_fingerprint/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/port_hopping_interval/others)* {
|
||||
proxy.type = "tuic";
|
||||
handleShadowTLS();
|
||||
}
|
||||
tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_fingerprint/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_fingerprint/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/port_hopping_interval/others)* {
|
||||
proxy.type = "tuic";
|
||||
proxy.version = 5;
|
||||
handleShadowTLS();
|
||||
@ -104,7 +104,7 @@ wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/under
|
||||
proxy.type = "wireguard-surge";
|
||||
handleShadowTLS();
|
||||
}
|
||||
hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/port_hopping_interval/others)* {
|
||||
proxy.type = "hysteria2";
|
||||
handleShadowTLS();
|
||||
}
|
||||
@ -151,6 +151,8 @@ port = digits:[0-9]+ {
|
||||
}
|
||||
}
|
||||
|
||||
port_hopping_interval = comma "port-hopping-interval" equals match:$[0-9]+ { proxy["hop-interval"] = parseInt(match.trim()); }
|
||||
|
||||
username = & {
|
||||
let j = peg$currPos;
|
||||
let start, end;
|
||||
|
@ -89,11 +89,11 @@ snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_
|
||||
}
|
||||
handleShadowTLS();
|
||||
}
|
||||
tuic = tag equals "tuic" address (alpn/token/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_fingerprint/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
tuic = tag equals "tuic" address (alpn/token/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_fingerprint/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/port_hopping_interval/others)* {
|
||||
proxy.type = "tuic";
|
||||
handleShadowTLS();
|
||||
}
|
||||
tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_fingerprint/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls_fingerprint/tls_verification/sni/fast_open/tfo/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/port_hopping_interval/others)* {
|
||||
proxy.type = "tuic";
|
||||
proxy.version = 5;
|
||||
handleShadowTLS();
|
||||
@ -102,7 +102,7 @@ wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/under
|
||||
proxy.type = "wireguard-surge";
|
||||
handleShadowTLS();
|
||||
}
|
||||
hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/port_hopping_interval/others)* {
|
||||
proxy.type = "hysteria2";
|
||||
handleShadowTLS();
|
||||
}
|
||||
@ -149,6 +149,8 @@ port = digits:[0-9]+ {
|
||||
}
|
||||
}
|
||||
|
||||
port_hopping_interval = comma "port-hopping-interval" equals match:$[0-9]+ { proxy["hop-interval"] = parseInt(match.trim()); }
|
||||
|
||||
username = & {
|
||||
let j = peg$currPos;
|
||||
let start, end;
|
||||
|
@ -675,6 +675,15 @@ function tuic(proxy) {
|
||||
'alpn',
|
||||
);
|
||||
|
||||
if (isPresent(proxy, 'ports')) {
|
||||
result.append(`,port-hopping=${proxy.ports.replace(/,/g, ';')}`);
|
||||
}
|
||||
|
||||
result.appendIfPresent(
|
||||
`,port-hopping-interval=${proxy['hop-interval']}`,
|
||||
'hop-interval',
|
||||
);
|
||||
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
||||
|
||||
@ -935,6 +944,15 @@ function hysteria2(proxy) {
|
||||
|
||||
result.appendIfPresent(`,password=${proxy.password}`, 'password');
|
||||
|
||||
if (isPresent(proxy, 'ports')) {
|
||||
result.append(`,port-hopping=${proxy.ports.replace(/,/g, ';')}`);
|
||||
}
|
||||
|
||||
result.appendIfPresent(
|
||||
`,port-hopping-interval=${proxy['hop-interval']}`,
|
||||
'hop-interval',
|
||||
);
|
||||
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user