Skip to content

Commit 3b4838e

Browse files
committed
feat: Loon AnyTLS/Trojon 支持 REALITY, SS2022 支持 obfs
1 parent 8a60b95 commit 3b4838e

5 files changed

Lines changed: 221 additions & 61 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.24.19",
3+
"version": "2.24.20",
44
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
55
"main": "src/main.js",
66
"packageManager": "pnpm@11.0.9",

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,11 @@ vless = tag equals "vless"i address uuid (transport/transport_host/transport_pat
8181
proxy.type = "vless";
8282
handleTransport();
8383
}
84-
trojan = tag equals "trojan"i address password (transport/transport_host/transport_path/over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/tls_profile/alpn/fast_open/udp_relay/ip_mode/block_quic/others)* {
84+
trojan = tag equals "trojan"i address password (transport/transport_host/transport_path/over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/tls_profile/alpn/fast_open/udp_relay/ip_mode/public_key/short_id/block_quic/others)* {
8585
proxy.type = "trojan";
8686
handleTransport();
8787
}
88-
anytls = tag equals "anytls"i address password (transport/transport_host/transport_path/over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/tls_profile/alpn/fast_open/udp_relay/ip_mode/block_quic/idle_session_check_interval/idle_session_timeout/min_idle_session/max_stream_count/others)* {
88+
anytls = tag equals "anytls"i address password (transport/transport_host/transport_path/over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/tls_profile/alpn/fast_open/udp_relay/ip_mode/public_key/short_id/block_quic/idle_session_check_interval/idle_session_timeout/min_idle_session/max_stream_count/others)* {
8989
proxy.type = "anytls";
9090
handleTransport();
9191
}

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

Lines changed: 46 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,9 @@ export default function Loon_Producer() {
4444
return hysteria2(proxy);
4545
}
4646
if (proxy.type === 'anytls') {
47-
if (
48-
proxy.network &&
49-
(!['tcp'].includes(proxy.network) ||
50-
(['tcp'].includes(proxy.network) && proxy['reality-opts']))
51-
) {
47+
if (proxy.network && !['tcp'].includes(proxy.network)) {
5248
throw new Error(
53-
`Platform ${targetPlatform} does not support proxy type ${proxy.type} with network or REALITY`,
49+
`Platform ${targetPlatform} does not support proxy type ${proxy.type} with network ${proxy.network}`,
5450
);
5551
}
5652

@@ -73,6 +69,18 @@ function appendAlpn(result, proxy) {
7369
if (alpn) result.append(`,alpn="${alpn}"`);
7470
}
7571

72+
function appendReality(result, proxy) {
73+
result.appendIfPresent(`,sni=${proxy.sni}`, 'sni');
74+
result.appendIfPresent(
75+
`,public-key="${proxy['reality-opts']['public-key']}"`,
76+
'reality-opts.public-key',
77+
);
78+
result.appendIfPresent(
79+
`,short-id=${proxy['reality-opts']['short-id']}`,
80+
'reality-opts.short-id',
81+
);
82+
}
83+
7684
function getLoonAlpn(proxy) {
7785
const values = Array.isArray(proxy.alpn)
7886
? proxy.alpn
@@ -134,14 +142,6 @@ function shadowsocks(proxy) {
134142
// obfs
135143
if (isPresent(proxy, 'plugin')) {
136144
if (proxy.plugin === 'obfs') {
137-
if (
138-
proxy['plugin-opts']?.mode &&
139-
proxy.cipher.startsWith('2022-')
140-
) {
141-
throw new Error(
142-
`${proxy.cipher} ${proxy.plugin} is not supported`,
143-
);
144-
}
145145
result.append(`,obfs-name=${proxy['plugin-opts'].mode}`);
146146
result.appendIfPresent(
147147
`,obfs-host=${proxy['plugin-opts'].host}`,
@@ -311,6 +311,7 @@ function shadowsocksr(proxy) {
311311
}
312312

313313
function trojan(proxy) {
314+
const isReality = !!proxy['reality-opts'];
314315
const result = new Result(proxy);
315316
result.append(
316317
`${proxy.name}=trojan,${proxy.server},${proxy.port},"${proxy.password}"`,
@@ -343,16 +344,20 @@ function trojan(proxy) {
343344
appendTlsProfile(result, proxy);
344345
appendAlpn(result, proxy);
345346

346-
// sni
347-
result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni');
348-
result.appendIfPresent(
349-
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
350-
'tls-fingerprint',
351-
);
352-
result.appendIfPresent(
353-
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
354-
'tls-pubkey-sha256',
355-
);
347+
if (isReality) {
348+
appendReality(result, proxy);
349+
} else {
350+
// sni
351+
result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni');
352+
result.appendIfPresent(
353+
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
354+
'tls-fingerprint',
355+
);
356+
result.appendIfPresent(
357+
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
358+
'tls-pubkey-sha256',
359+
);
360+
}
356361

357362
// tfo
358363
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
@@ -375,6 +380,7 @@ function trojan(proxy) {
375380
}
376381

377382
function anytls(proxy) {
383+
const isReality = !!proxy['reality-opts'];
378384
const result = new Result(proxy);
379385
result.append(
380386
`${proxy.name}=anytls,${proxy.server},${proxy.port},"${proxy.password}"`,
@@ -400,16 +406,20 @@ function anytls(proxy) {
400406
appendTlsProfile(result, proxy);
401407
appendAlpn(result, proxy);
402408

403-
// sni
404-
result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni');
405-
result.appendIfPresent(
406-
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
407-
'tls-fingerprint',
408-
);
409-
result.appendIfPresent(
410-
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
411-
'tls-pubkey-sha256',
412-
);
409+
if (isReality) {
410+
appendReality(result, proxy);
411+
} else {
412+
// sni
413+
result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni');
414+
result.appendIfPresent(
415+
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
416+
'tls-fingerprint',
417+
);
418+
result.appendIfPresent(
419+
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
420+
'tls-pubkey-sha256',
421+
);
422+
}
413423

414424
// tfo
415425
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
@@ -487,15 +497,7 @@ function vmess(proxy) {
487497
}
488498

489499
if (isReality) {
490-
result.appendIfPresent(`,sni=${proxy.sni}`, 'sni');
491-
result.appendIfPresent(
492-
`,public-key="${proxy['reality-opts']['public-key']}"`,
493-
'reality-opts.public-key',
494-
);
495-
result.appendIfPresent(
496-
`,short-id=${proxy['reality-opts']['short-id']}`,
497-
'reality-opts.short-id',
498-
);
500+
appendReality(result, proxy);
499501
} else {
500502
// sni
501503
result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni');
@@ -604,15 +606,7 @@ function vless(proxy) {
604606
result.appendIfPresent(`,flow=${proxy.flow}`, 'flow');
605607
}
606608
if (isReality) {
607-
result.appendIfPresent(`,sni=${proxy.sni}`, 'sni');
608-
result.appendIfPresent(
609-
`,public-key="${proxy['reality-opts']['public-key']}"`,
610-
'reality-opts.public-key',
611-
);
612-
result.appendIfPresent(
613-
`,short-id=${proxy['reality-opts']['short-id']}`,
614-
'reality-opts.short-id',
615-
);
609+
appendReality(result, proxy);
616610
} else {
617611
// sni
618612
result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni');

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

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1984,9 +1984,7 @@ describe('VMess and VLESS parser coverage', function () {
19841984
expect(proxy['xhttp-opts']).to.not.have.property(
19851985
'uplink-data-placement',
19861986
);
1987-
expect(proxy['xhttp-opts']).to.not.have.property(
1988-
'uplink-data-key',
1989-
);
1987+
expect(proxy['xhttp-opts']).to.not.have.property('uplink-data-key');
19901988
expect(
19911989
proxy['xhttp-opts']?.['download-settings'],
19921990
).to.not.have.property('no-grpc-header');
@@ -2165,9 +2163,7 @@ describe('VMess and VLESS parser coverage', function () {
21652163
},
21662164
},
21672165
});
2168-
expect(proxy['xhttp-opts']).to.not.have.property(
2169-
'x-padding-bytes',
2170-
);
2166+
expect(proxy['xhttp-opts']).to.not.have.property('x-padding-bytes');
21712167
expect(proxy['xhttp-opts']).to.not.have.property(
21722168
'uplink-chunk-size',
21732169
);
@@ -2878,6 +2874,24 @@ describe('Platform raw-format parser coverage', function () {
28782874
},
28792875
},
28802876
},
2877+
{
2878+
title: 'parses shadowsocks 2022 obfs http lines',
2879+
input: 'Loon SS2022=shadowsocks,loon-ss2022.example.com,8388,2022-blake3-aes-128-gcm,"server-key:user-key",obfs-name=http,obfs-host=obfs.example.com,obfs-uri=/',
2880+
expected: {
2881+
type: 'ss',
2882+
name: 'Loon SS2022',
2883+
server: 'loon-ss2022.example.com',
2884+
port: 8388,
2885+
cipher: '2022-blake3-aes-128-gcm',
2886+
password: 'server-key:user-key',
2887+
plugin: 'obfs',
2888+
'plugin-opts': {
2889+
mode: 'http',
2890+
host: 'obfs.example.com',
2891+
path: '/',
2892+
},
2893+
},
2894+
},
28812895
{
28822896
title: 'parses shadowsocksr lines',
28832897
input: 'Loon SSR=shadowsocksr,loon-ssr.example.com,8389,aes-256-cfb,"secret",protocol=auth_chain_b,protocol-param=device-id,obfs=tls1.2_ticket_fastauth,obfs-param=cdn.example.com',
@@ -2920,6 +2934,26 @@ describe('Platform raw-format parser coverage', function () {
29202934
},
29212935
},
29222936
},
2937+
{
2938+
title: 'parses vmess reality lines',
2939+
input: `Loon VMess Reality=vmess,loon-vmess-reality.example.com,443,auto,"${UUID}",transport=tcp,over-tls=true,sni=sni.example.com,skip-cert-verify=true,public-key=vmess-pubkey,short-id=01,alterId=0`,
2940+
expected: {
2941+
type: 'vmess',
2942+
name: 'Loon VMess Reality',
2943+
server: 'loon-vmess-reality.example.com',
2944+
port: 443,
2945+
cipher: 'auto',
2946+
uuid: UUID,
2947+
alterId: 0,
2948+
tls: true,
2949+
sni: 'sni.example.com',
2950+
'skip-cert-verify': true,
2951+
'reality-opts': {
2952+
'public-key': 'vmess-pubkey',
2953+
'short-id': '01',
2954+
},
2955+
},
2956+
},
29232957
{
29242958
title: 'canonicalizes Loon vmess chacha20 security',
29252959
input: `Loon VMess Chacha=vmess,loon-vmess-chacha.example.com,443,chacha20-ietf-poly1305,"${UUID}"`,
@@ -2993,6 +3027,24 @@ describe('Platform raw-format parser coverage', function () {
29933027
},
29943028
},
29953029
},
3030+
{
3031+
title: 'parses trojan reality lines',
3032+
input: 'Loon Trojan Reality=trojan,loon-trojan-reality.example.com,443,"secret",over-tls=true,sni=sni.example.com,skip-cert-verify=true,public-key=trojan-pubkey,short-id=02',
3033+
expected: {
3034+
type: 'trojan',
3035+
name: 'Loon Trojan Reality',
3036+
server: 'loon-trojan-reality.example.com',
3037+
port: 443,
3038+
password: 'secret',
3039+
tls: true,
3040+
sni: 'sni.example.com',
3041+
'skip-cert-verify': true,
3042+
'reality-opts': {
3043+
'public-key': 'trojan-pubkey',
3044+
'short-id': '02',
3045+
},
3046+
},
3047+
},
29963048
{
29973049
title: 'parses anytls lines',
29983050
input: 'Loon AnyTLS=anytls,loon-anytls.example.com,443,"secret",transport=ws,host=cdn.example.com,path=/anytls,over-tls=true,tls-name=sni.example.com,skip-cert-verify=true,idle-session-timeout=30,max-stream-count=16',
@@ -3016,6 +3068,24 @@ describe('Platform raw-format parser coverage', function () {
30163068
'max-stream-count': 16,
30173069
},
30183070
},
3071+
{
3072+
title: 'parses anytls reality lines',
3073+
input: 'Loon AnyTLS Reality=anytls,loon-anytls-reality.example.com,443,"secret",over-tls=true,sni=sni.example.com,skip-cert-verify=true,public-key=anytls-pubkey,short-id=03',
3074+
expected: {
3075+
type: 'anytls',
3076+
name: 'Loon AnyTLS Reality',
3077+
server: 'loon-anytls-reality.example.com',
3078+
port: 443,
3079+
password: 'secret',
3080+
tls: true,
3081+
sni: 'sni.example.com',
3082+
'skip-cert-verify': true,
3083+
'reality-opts': {
3084+
'public-key': 'anytls-pubkey',
3085+
'short-id': '03',
3086+
},
3087+
},
3088+
},
30193089
{
30203090
title: 'parses hysteria2 lines',
30213091
input: 'Loon Hysteria2=hysteria2,loon-hy2.example.com,443,"secret",tls-name=peer.example.com,skip-cert-verify=true,download-bandwidth=100,salamander-password=mask,ecn=true',

0 commit comments

Comments
 (0)