Skip to content

Commit b770578

Browse files
authored
Merge pull request #228 from xream/feature/tuic
feat: Added support for tuic and some compatibility adjustments
2 parents e99f13d + 47a95e5 commit b770578

File tree

7 files changed

+125
-8
lines changed

7 files changed

+125
-8
lines changed

backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "sub-store",
3-
"version": "2.14.4",
3+
"version": "2.14.5",
44
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
55
"main": "src/main.js",
66
"scripts": {

backend/src/core/proxy-utils/parsers/index.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ function Clash_All() {
270270
'http',
271271
'snell',
272272
'trojan',
273+
'tuic',
273274
].includes(proxy.type)
274275
) {
275276
throw new Error(
@@ -474,6 +475,15 @@ function Surge_Snell() {
474475
return { name, test, parse };
475476
}
476477

478+
function Surge_Tuic() {
479+
const name = 'Surge Tuic Parser';
480+
const test = (line) => {
481+
return /^.*=\s*tuic(-v5)??/.test(line.split(',')[0]);
482+
};
483+
const parse = (line) => getSurgeParser().parse(line);
484+
return { name, test, parse };
485+
}
486+
477487
export default [
478488
URI_SS(),
479489
URI_SSR(),
@@ -485,6 +495,7 @@ export default [
485495
Surge_Trojan(),
486496
Surge_Http(),
487497
Surge_Snell(),
498+
Surge_Tuic(),
488499
Surge_Socks5(),
489500
Loon_SS(),
490501
Loon_SSR(),

backend/src/core/proxy-utils/parsers/peggy/surge.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const grammars = String.raw`
2929
}
3030
}
3131
32-
start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls) {
32+
start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v5) {
3333
return proxy;
3434
}
3535
@@ -73,6 +73,13 @@ snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_
7373
$set(proxy, "obfs-opts.path", obfs.path);
7474
}
7575
}
76+
tuic = tag equals "tuic" address (alpn/token/ip_version/tls_verification/sni/fast_open/tfo/others)* {
77+
proxy.type = "tuic";
78+
}
79+
tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/tls_verification/sni/fast_open/tfo/others)* {
80+
proxy.type = "tuic";
81+
proxy.version = 5;
82+
}
7683
socks5 = tag equals "socks5" address (username password)? (fast_open/others)* {
7784
proxy.type = "socks5";
7885
}
@@ -175,6 +182,11 @@ uri = $[^,]+
175182
176183
udp_relay = comma "udp" equals flag:bool { proxy.udp = flag; }
177184
fast_open = comma "fast-open" equals flag:bool { proxy.tfo = flag; }
185+
tfo = comma "tfo" equals flag:bool { proxy.tfo = flag; }
186+
ip_version = comma "ip-version" equals match:[^,]+ { proxy["ip-version"] = match.join(""); }
187+
token = comma "token" equals match:[^,]+ { proxy.token = match.join(""); }
188+
alpn = comma "alpn" equals match:[^,]+ { proxy.alpn = match.join(""); }
189+
uuidk = comma "uuid" equals match:[^,]+ { proxy.uuid = match.join(""); }
178190
179191
tag = match:[^=,]* { proxy.name = match.join("").trim(); }
180192
comma = _ "," _

backend/src/core/proxy-utils/parsers/peggy/surge.peg

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
}
2828
}
2929

30-
start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls) {
30+
start = (shadowsocks/vmess/trojan/https/http/snell/socks5/socks5_tls/tuic/tuic_v5) {
3131
return proxy;
3232
}
3333

@@ -71,6 +71,13 @@ snell = tag equals "snell" address (snell_version/snell_psk/obfs/obfs_host/obfs_
7171
$set(proxy, "obfs-opts.path", obfs.path);
7272
}
7373
}
74+
tuic = tag equals "tuic" address (alpn/token/ip_version/tls_verification/sni/fast_open/tfo/others)* {
75+
proxy.type = "tuic";
76+
}
77+
tuic_v5 = tag equals "tuic-v5" address (alpn/passwordk/uuidk/ip_version/tls_verification/sni/fast_open/tfo/others)* {
78+
proxy.type = "tuic";
79+
proxy.version = 5;
80+
}
7481
socks5 = tag equals "socks5" address (username password)? (fast_open/others)* {
7582
proxy.type = "socks5";
7683
}
@@ -173,6 +180,11 @@ uri = $[^,]+
173180

174181
udp_relay = comma "udp" equals flag:bool { proxy.udp = flag; }
175182
fast_open = comma "fast-open" equals flag:bool { proxy.tfo = flag; }
183+
tfo = comma "tfo" equals flag:bool { proxy.tfo = flag; }
184+
ip_version = comma "ip-version" equals match:[^,]+ { proxy["ip-version"] = match.join(""); }
185+
token = comma "token" equals match:[^,]+ { proxy.token = match.join(""); }
186+
alpn = comma "alpn" equals match:[^,]+ { proxy.alpn = match.join(""); }
187+
uuidk = comma "uuid" equals match:[^,]+ { proxy.uuid = match.join(""); }
176188

177189
tag = match:[^=,]* { proxy.name = match.join("").trim(); }
178190
comma = _ "," _

backend/src/core/proxy-utils/producers/clash.js

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,27 @@ export default function Clash_Producer() {
44
const type = 'ALL';
55
const produce = (proxies) => {
66
// filter unsupported proxies
7-
proxies = proxies.filter((proxy) =>
8-
['ss', 'ssr', 'vmess', 'socks', 'http', 'snell', 'trojan'].includes(
9-
proxy.type,
10-
),
11-
);
7+
proxies = proxies.filter((proxy) => {
8+
if (
9+
![
10+
'ss',
11+
'ssr',
12+
'vmess',
13+
'socks',
14+
'http',
15+
'snell',
16+
'trojan',
17+
].includes(proxy.type)
18+
) {
19+
return false;
20+
} else if (
21+
proxy.type === 'snell' &&
22+
String(proxy.version) === '4'
23+
) {
24+
return false;
25+
}
26+
return true;
27+
});
1228
return (
1329
'proxies:\n' +
1430
proxies

backend/src/core/proxy-utils/producers/stash.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ export default function Stash_Producer() {
66
return (
77
'proxies:\n' +
88
proxies
9+
.filter((proxy) => {
10+
if (
11+
proxy.type === 'snell' &&
12+
String(proxy.version) === '4'
13+
) {
14+
return false;
15+
}
16+
return true;
17+
})
918
.map((proxy) => {
1019
if (proxy.type === 'vmess') {
1120
// handle vmess aead
@@ -19,6 +28,14 @@ export default function Stash_Producer() {
1928
proxy.servername = proxy.sni;
2029
delete proxy.sni;
2130
}
31+
} else if (proxy.type === 'tuic') {
32+
if (isPresent(proxy, 'alpn')) {
33+
proxy.alpn = Array.isArray(proxy.alpn)
34+
? proxy.alpn
35+
: [proxy.alpn];
36+
} else {
37+
proxy.alpn = ['h3'];
38+
}
2239
}
2340

2441
delete proxy['tls-fingerprint'];

backend/src/core/proxy-utils/producers/surge.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ import $ from '@/core/app';
44

55
const targetPlatform = 'Surge';
66

7+
const ipVersions = {
8+
dual: 'dual',
9+
ipv4: 'v4-only',
10+
ipv6: 'v6-only',
11+
'ipv4-prefer': 'prefer-v4',
12+
'ipv6-prefer': 'prefer-v6',
13+
};
14+
715
export default function Surge_Producer() {
816
const produce = (proxy) => {
917
switch (proxy.type) {
@@ -19,6 +27,8 @@ export default function Surge_Producer() {
1927
return socks5(proxy);
2028
case 'snell':
2129
return snell(proxy);
30+
case 'tuic':
31+
return tuic(proxy);
2232
}
2333
throw new Error(
2434
`Platform ${targetPlatform} does not support proxy type: ${proxy.type}`,
@@ -239,6 +249,45 @@ function snell(proxy) {
239249
return result.toString();
240250
}
241251

252+
function tuic(proxy) {
253+
const result = new Result(proxy);
254+
let type = proxy.type;
255+
if (proxy.password && proxy.uuid) {
256+
type = 'tuic-v5';
257+
}
258+
result.append(`${proxy.name}=${type},${proxy.server},${proxy.port}`);
259+
260+
result.appendIfPresent(`,uuid=${proxy.uuid}`, 'uuid');
261+
result.appendIfPresent(`,password=${proxy.password}`, 'password');
262+
result.appendIfPresent(`,token=${proxy.token}`, 'token');
263+
264+
result.appendIfPresent(
265+
`,alpn=${Array.isArray(proxy.alpn) ? proxy.alpn[0] : proxy.alpn}`,
266+
'alpn',
267+
);
268+
269+
result.appendIfPresent(
270+
`,ip-version=${ipVersions[proxy['ip-version']] || proxy['ip-version']}`,
271+
'ip-version',
272+
);
273+
274+
// tls verification
275+
result.appendIfPresent(`,sni=${proxy.sni}`, 'sni');
276+
result.appendIfPresent(
277+
`,skip-cert-verify=${proxy['skip-cert-verify']}`,
278+
'skip-cert-verify',
279+
);
280+
281+
// tfo
282+
result.appendIfPresent(`,tfo=${proxy.tfo}`, 'fast-open');
283+
result.appendIfPresent(`,tfo=${proxy.tfo}`, 'tfo');
284+
285+
// test-url
286+
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
287+
288+
return result.toString();
289+
}
290+
242291
function handleTransport(result, proxy) {
243292
if (isPresent(proxy, 'network')) {
244293
if (proxy.network === 'ws') {

0 commit comments

Comments
 (0)