Skip to content

Commit 2b28e1b

Browse files
committed
feat: Surfboard 支持 TUIC v5
1 parent c8632ef commit 2b28e1b

3 files changed

Lines changed: 73 additions & 1 deletion

File tree

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ export default function Surfboard_Producer() {
4646
return http(proxy);
4747
case 'snell':
4848
return snell(proxy);
49+
case 'tuic':
50+
return tuic(proxy);
4951
case 'socks5':
5052
return socks5(proxy);
5153
case 'hysteria2':
@@ -72,6 +74,35 @@ export default function Surfboard_Producer() {
7274
};
7375
return { produce };
7476
}
77+
function tuic(proxy) {
78+
if (proxy.token?.length) {
79+
throw new Error(
80+
`Platform ${targetPlatform} does not support proxy type ${proxy.type} v4`,
81+
);
82+
}
83+
const result = new Result(proxy);
84+
result.append(`${proxy.name}=tuic-v5,${proxy.server},${proxy.port}`);
85+
result.appendIfPresent(`,uuid=${proxy.uuid}`, 'uuid');
86+
result.appendIfPresent(`,password="${proxy.password}"`, 'password');
87+
if (hasNonBlankValue(proxy.alpn)) {
88+
result.append(
89+
`,alpn="${
90+
Array.isArray(proxy.alpn) ? proxy.alpn.join(',') : proxy.alpn
91+
}"`,
92+
);
93+
}
94+
if (hasNonBlankValue(proxy.ports)) {
95+
result.append(
96+
`,port-hopping="${String(proxy.ports).replace(/,/g, ';')}"`,
97+
);
98+
}
99+
if (hasNonBlankValue(proxy['hop-interval'])) {
100+
result.append(`,port-hopping-interval=${proxy['hop-interval']}`);
101+
}
102+
appendTlsProxyParams(result, proxy);
103+
result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
104+
return result.toString();
105+
}
75106
function hysteria2(proxy) {
76107
if (
77108
(proxy.obfs && proxy.obfs !== 'salamander') ||

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -973,7 +973,7 @@ function snell(proxy) {
973973

974974
function tuic(proxy) {
975975
const result = new Result(proxy);
976-
// https://github.com/MetaCubeX/Clash.Meta/blob/Alpha/adapter/outbound/tuic.go#L197
976+
// https://github.com/MetaCubeX/mihomo/blob/e26714a181ac0e2fa803453c0a8e9a9ce94e31cb/adapter/outbound/tuic.go#L271
977977
let type = proxy.type;
978978
if (!proxy.token || proxy.token.length === 0) {
979979
type = 'tuic-v5';

backend/src/test/proxy-producers/text.spec.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1635,6 +1635,47 @@ describe('Proxy text producers', function () {
16351635
);
16361636
});
16371637

1638+
it('produces Surfboard TUIC v5 lines', function () {
1639+
const output = produceExternal('Surfboard', {
1640+
type: 'tuic',
1641+
name: 'ProxyTuic',
1642+
server: '1.2.3.4',
1643+
port: 443,
1644+
uuid: UUID,
1645+
password: 'pwd',
1646+
alpn: ['h3'],
1647+
ports: '1234,5000-6000',
1648+
'hop-interval': 30,
1649+
'skip-cert-verify': true,
1650+
sni: 'example.com',
1651+
'tls-fingerprint':
1652+
'fac26f65c034829da42d740d23c4a7202475a3834f0ebaecae5f934adbbfd640',
1653+
udp: true,
1654+
});
1655+
1656+
expect(output).to.equal(
1657+
`ProxyTuic=tuic-v5,1.2.3.4,443,uuid=${UUID},password="pwd",alpn="h3",port-hopping="1234;5000-6000",port-hopping-interval=30,server-cert-fingerprint-sha256=fac26f65c034829da42d740d23c4a7202475a3834f0ebaecae5f934adbbfd640,sni="example.com",skip-cert-verify=true,udp-relay=true`,
1658+
);
1659+
});
1660+
1661+
it('does not produce legacy TUIC as Surfboard TUIC v5', function () {
1662+
const output = ProxyUtils.produce(
1663+
[
1664+
{
1665+
type: 'tuic',
1666+
name: 'Legacy TUIC',
1667+
server: 'tuic.example.com',
1668+
port: 443,
1669+
token: 'secret',
1670+
},
1671+
],
1672+
'Surfboard',
1673+
'external',
1674+
);
1675+
1676+
expect(output).to.equal('');
1677+
});
1678+
16381679
it('does not emit empty Surfboard Hysteria2 port hopping fields', function () {
16391680
const output = produceExternal('Surfboard', {
16401681
type: 'hysteria2',

0 commit comments

Comments
 (0)