Skip to content

Commit 7188d29

Browse files
committed
feat: mihomo 校验 ShadowTLS, 支持新版 Snell
1 parent 5007a34 commit 7188d29

3 files changed

Lines changed: 148 additions & 2 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.23.18",
3+
"version": "2.23.19",
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/producers/clashmeta.js

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,19 @@ export default function ClashMeta_Producer() {
6868
}
6969
if (!supportsShadowsocksV2rayPluginMode(proxy, ['websocket'])) {
7070
return false;
71-
} else if (proxy.type === 'snell' && proxy.version >= 4) {
71+
} else if (
72+
proxy.type === 'snell' &&
73+
!isSupportedMihomoVersion(proxy.version, [1, 2, 3, 4, 5])
74+
) {
75+
return false;
76+
} else if (
77+
hasMihomoShadowTls(proxy) &&
78+
(proxy.type !== 'ss' ||
79+
!isSupportedMihomoVersion(
80+
getMihomoShadowTlsVersion(proxy),
81+
[1, 2, 3],
82+
))
83+
) {
7284
return false;
7385
} else if (['juicity', 'naive'].includes(proxy.type)) {
7486
return false;
@@ -394,3 +406,39 @@ function hasRootHeaders(proxy) {
394406
Object.keys(proxy.headers).length > 0
395407
);
396408
}
409+
410+
function isSupportedMihomoVersion(version, supportedVersions) {
411+
if (version == null) {
412+
return true;
413+
}
414+
415+
const normalized =
416+
typeof version === 'string' ? version.trim() : `${version}`;
417+
if (!normalized) {
418+
return false;
419+
}
420+
421+
const parsed = Number(normalized);
422+
return Number.isInteger(parsed) && supportedVersions.includes(parsed);
423+
}
424+
425+
function hasMihomoShadowTls(proxy) {
426+
return (
427+
proxy?.plugin === 'shadow-tls' ||
428+
isPresent(proxy, 'shadow-tls-password') ||
429+
isPresent(proxy, 'shadow-tls-sni') ||
430+
isPresent(proxy, 'shadow-tls-version')
431+
);
432+
}
433+
434+
function getMihomoShadowTlsVersion(proxy) {
435+
if (isPresent(proxy, 'shadow-tls-version')) {
436+
return proxy['shadow-tls-version'];
437+
}
438+
439+
if (proxy?.plugin === 'shadow-tls') {
440+
return proxy?.['plugin-opts']?.version;
441+
}
442+
443+
return undefined;
444+
}

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

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,104 @@ describe('Proxy structured producers', function () {
161161
}
162162
});
163163

164+
it('keeps Mihomo Snell versions 1 through 5', function () {
165+
const proxies = [1, 2, 3, 4, 5, 6].map((version) => ({
166+
type: 'snell',
167+
name: `Snell ${version}`,
168+
server: 'snell.example.com',
169+
port: 44046,
170+
psk: 'secret',
171+
version,
172+
udp: true,
173+
}));
174+
175+
const internal = produceInternal('Mihomo', proxies);
176+
const external = loadProducedYaml('Mihomo', proxies);
177+
178+
expect(internal.map((proxy) => proxy.version)).to.deep.equal([
179+
1, 2, 3, 4, 5,
180+
]);
181+
expect(external.proxies.map((proxy) => proxy.version)).to.deep.equal([
182+
1, 2, 3, 4, 5,
183+
]);
184+
expect(
185+
internal.find((proxy) => proxy.version === 1),
186+
).to.not.have.property('udp');
187+
expect(
188+
internal.find((proxy) => proxy.version === 2),
189+
).to.not.have.property('udp');
190+
expect(internal.find((proxy) => proxy.version === 4).udp).to.equal(
191+
true,
192+
);
193+
expect(internal.find((proxy) => proxy.version === 5).udp).to.equal(
194+
true,
195+
);
196+
});
197+
198+
it('keeps only Shadowsocks shadow-tls versions 1 through 3 for Mihomo', function () {
199+
const buildShadowTlsProxy = (name, version) => ({
200+
type: 'ss',
201+
name,
202+
server: 'ss.example.com',
203+
port: 8388,
204+
cipher: 'aes-128-gcm',
205+
password: 'secret',
206+
'shadow-tls-password': 'shadow-pass',
207+
'shadow-tls-sni': 'mask.example.com',
208+
'shadow-tls-version': version,
209+
});
210+
const proxies = [
211+
buildShadowTlsProxy('ShadowTLS 1', 1),
212+
{
213+
type: 'ss',
214+
name: 'ShadowTLS 2',
215+
server: 'ss.example.com',
216+
port: 8388,
217+
cipher: 'aes-128-gcm',
218+
password: 'secret',
219+
plugin: 'shadow-tls',
220+
'plugin-opts': {
221+
host: 'mask.example.com',
222+
password: 'shadow-pass',
223+
version: 2,
224+
},
225+
},
226+
buildShadowTlsProxy('ShadowTLS 3', 3),
227+
buildShadowTlsProxy('ShadowTLS 4', 4),
228+
{
229+
type: 'vmess',
230+
name: 'VMess ShadowTLS',
231+
server: 'vmess.example.com',
232+
port: 443,
233+
uuid: UUID,
234+
cipher: 'auto',
235+
plugin: 'shadow-tls',
236+
'plugin-opts': {
237+
host: 'mask.example.com',
238+
password: 'shadow-pass',
239+
version: 3,
240+
},
241+
},
242+
];
243+
244+
const internal = produceInternal('Mihomo', proxies);
245+
const external = loadProducedYaml('Mihomo', proxies);
246+
247+
expect(internal.map((proxy) => proxy.name)).to.deep.equal([
248+
'ShadowTLS 1',
249+
'ShadowTLS 2',
250+
'ShadowTLS 3',
251+
]);
252+
expect(
253+
internal.map((proxy) => proxy['plugin-opts'].version),
254+
).to.deep.equal([1, 2, 3]);
255+
expect(external.proxies.map((proxy) => proxy.name)).to.deep.equal([
256+
'ShadowTLS 1',
257+
'ShadowTLS 2',
258+
'ShadowTLS 3',
259+
]);
260+
});
261+
164262
it('keeps only supported shadowsocks v2ray-plugin modes for Shadowrocket by default', function () {
165263
const buildProxy = (name, mode) => ({
166264
type: 'ss',

0 commit comments

Comments
 (0)