Skip to content

Commit bdd5635

Browse files
committed
feat: 完善 VLESS/Trojan URI 与 mihomo 转换
1 parent d6b0791 commit bdd5635

7 files changed

Lines changed: 107 additions & 5 deletions

File tree

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.21.84",
3+
"version": "2.21.85",
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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -868,7 +868,7 @@ function URI_VLESS() {
868868
proxy.alpn = params.alpn ? params.alpn.split(',') : undefined;
869869
proxy['skip-cert-verify'] = /(TRUE)|1/i.test(params.allowInsecure);
870870
proxy._echConfigList = getIfPresent(params.ech);
871-
proxy._pcs = getIfPresent(params.pcs);
871+
proxy['tls-fingerprint'] = getIfPresent(params.pcs);
872872
proxy._h2 = /(TRUE)|1/i.test(params.h2);
873873

874874
switch (`${params.packetEncoding || ''}`.toLowerCase()) {

backend/src/core/proxy-utils/parsers/peggy/trojan-uri.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ params = "?" head:param tail:("&"@param)* {
8686
proxy["skip-cert-verify"] = toBool(params["allowInsecure"]);
8787
proxy.sni = params["sni"] || params["peer"];
8888
proxy['client-fingerprint'] = params.fp;
89+
proxy['tls-fingerprint'] = params.pcs;
8990
proxy.alpn = params.alpn ? decodeURIComponent(params.alpn).split(',') : undefined;
9091
9192
if (toBool(params["ws"])) {

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,8 @@ function vless(proxy) {
194194
h2 = `&h2=1`;
195195
}
196196
let pcs = '';
197-
if (proxy._pcs) {
198-
pcs = `&pcs=${encodeURIComponent(proxy._pcs)}`;
197+
if (proxy['tls-fingerprint']) {
198+
pcs = `&pcs=${encodeURIComponent(proxy['tls-fingerprint'])}`;
199199
}
200200
let ech = '';
201201
if (proxy._echConfigList) {
@@ -697,6 +697,12 @@ export default function URI_Producer() {
697697
proxy['client-fingerprint'],
698698
)}`;
699699
}
700+
let trojanPcs = '';
701+
if (proxy['tls-fingerprint']) {
702+
trojanPcs = `&pcs=${encodeURIComponent(
703+
proxy['tls-fingerprint'],
704+
)}`;
705+
}
700706
let trojanAlpn = '';
701707
if (proxy.alpn) {
702708
trojanAlpn = `&alpn=${encodeURIComponent(
@@ -739,7 +745,7 @@ export default function URI_Producer() {
739745
proxy.port
740746
}?sni=${encodeURIComponent(proxy.sni || proxy.server)}${
741747
proxy['skip-cert-verify'] ? '&allowInsecure=1' : ''
742-
}${trojanTransport}${trojanAlpn}${trojanFp}${trojanSecurity}${trojanSid}${trojanPbk}${trojanSpx}${trojanMode}${trojanExtra}#${encodeURIComponent(
748+
}${trojanTransport}${trojanAlpn}${trojanFp}${trojanPcs}${trojanSecurity}${trojanSid}${trojanPbk}${trojanSpx}${trojanMode}${trojanExtra}#${encodeURIComponent(
743749
proxy.name,
744750
)}`;
745751
break;

backend/src/test/proxy-parsers/uri.spec.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,28 @@ describe('Proxy URI parser coverage', function () {
367367
});
368368
});
369369

370+
it('parses Trojan URIs with pcs as tls fingerprint', function () {
371+
const proxy = parseOne(
372+
'trojan://trojan-pass@trojan-ws.example.com?type=ws&host=ws.example.com&path=%2Fws&pcs=fingerprint#Trojan%20WS%20PCS',
373+
);
374+
375+
expectSubset(proxy, {
376+
type: 'trojan',
377+
name: 'Trojan WS PCS',
378+
server: 'trojan-ws.example.com',
379+
port: 443,
380+
password: 'trojan-pass',
381+
'tls-fingerprint': 'fingerprint',
382+
network: 'ws',
383+
'ws-opts': {
384+
path: '/ws',
385+
headers: {
386+
Host: 'ws.example.com',
387+
},
388+
},
389+
});
390+
});
391+
370392
it('parses Trojan URIs with grpc reality metadata', function () {
371393
const proxy = parseOne(
372394
'trojan://trojan-pass@trojan-grpc.example.com?type=grpc&serviceName=grpc-service&authority=grpc.example.com&mode=multi&security=reality&pbk=pubkey&sid=08&spx=%2Fspider&udp=1&tfo=1#Trojan%20Reality',

backend/src/test/proxy-parsers/v2ray-and-platforms.spec.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,31 @@ describe('VMess and VLESS parser coverage', function () {
284284
expect(proxy).to.not.have.property('xudp');
285285
});
286286

287+
it('parses websocket VLESS shares with pcs as tls fingerprint', function () {
288+
const proxy = parseOne(
289+
`vless://${UUID}@vless-ws.example.com:443?type=ws&security=tls&host=cdn.example.com&path=%2Fws&pcs=fingerprint#VLESS%20WS%20PCS`,
290+
);
291+
292+
expectSubset(proxy, {
293+
type: 'vless',
294+
name: 'VLESS WS PCS',
295+
server: 'vless-ws.example.com',
296+
port: 443,
297+
uuid: UUID,
298+
tls: true,
299+
udp: true,
300+
xudp: true,
301+
'tls-fingerprint': 'fingerprint',
302+
network: 'ws',
303+
'ws-opts': {
304+
path: '/ws',
305+
headers: {
306+
Host: 'cdn.example.com',
307+
},
308+
},
309+
});
310+
});
311+
287312
it('rejects websocket VLESS shares with malformed early data size', function () {
288313
expect(
289314
parseAll(

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,30 @@ describe('Proxy text producers', function () {
475475
);
476476
});
477477

478+
it('produces URI VLESS websocket links with pcs from tls fingerprint', function () {
479+
const output = produceExternal('URI', {
480+
type: 'vless',
481+
name: 'URI WS PCS',
482+
server: 'vless.example.com',
483+
port: 443,
484+
uuid: UUID,
485+
tls: true,
486+
sni: 'sni.example.com',
487+
'tls-fingerprint': 'fingerprint',
488+
network: 'ws',
489+
'ws-opts': {
490+
path: '/ws',
491+
headers: {
492+
Host: 'cdn.example.com',
493+
},
494+
},
495+
});
496+
497+
expect(output).to.equal(
498+
`vless://${UUID}@vless.example.com:443?security=tls&type=ws&path=%2Fws&host=cdn.example.com&pcs=fingerprint&sni=sni.example.com#URI%20WS%20PCS`,
499+
);
500+
});
501+
478502
it('produces URI VLESS fake-http links with method and headerType', function () {
479503
const output = produceExternal('URI', {
480504
type: 'vless',
@@ -687,6 +711,30 @@ describe('Proxy text producers', function () {
687711
);
688712
});
689713

714+
it('produces URI Trojan websocket links with pcs from tls fingerprint', function () {
715+
const output = produceExternal('URI', {
716+
type: 'trojan',
717+
name: 'URI Trojan PCS',
718+
server: 'trojan.example.com',
719+
port: 443,
720+
password: 'secret',
721+
tls: true,
722+
sni: 'sni.example.com',
723+
'tls-fingerprint': 'fingerprint',
724+
network: 'ws',
725+
'ws-opts': {
726+
path: '/ws',
727+
headers: {
728+
Host: 'cdn.example.com',
729+
},
730+
},
731+
});
732+
733+
expect(output).to.equal(
734+
'trojan://secret@trojan.example.com:443?sni=sni.example.com&type=ws&path=%2Fws&host=cdn.example.com&pcs=fingerprint#URI%20Trojan%20PCS',
735+
);
736+
});
737+
690738
it('produces V2Ray exports as base64 encoded URI lists', function () {
691739
const output = produceExternal('V2Ray', {
692740
type: 'vless',

0 commit comments

Comments
 (0)