Skip to content

Commit 1d5d25b

Browse files
committed
feat: 支持 mihomo VMess/VLESS/Trojan/AnyTLS 配合 ShadowTLS
1 parent 25d1077 commit 1d5d25b

5 files changed

Lines changed: 387 additions & 6 deletions

File tree

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,18 @@ function lastParse(proxy) {
781781
.replace(/^\[/, '')
782782
.replace(/\]$/, '');
783783
}
784+
if (
785+
['vmess', 'vless', 'trojan', 'anytls'].includes(proxy.type) &&
786+
proxy['shadow-tls-opts']
787+
) {
788+
proxy.plugin = 'shadow-tls';
789+
proxy['plugin-opts'] = {
790+
host: proxy.sni,
791+
password: proxy['shadow-tls-opts'].password,
792+
version: proxy['shadow-tls-opts'].version,
793+
};
794+
delete proxy['shadow-tls-opts'];
795+
}
784796
if (
785797
proxy.type === 'snell' &&
786798
proxy['obfs-opts']?.mode === 'shadow-tls' &&
@@ -801,6 +813,32 @@ function lastParse(proxy) {
801813
}
802814
delete proxy.alpn;
803815
}
816+
const xhttpDownloadSettings =
817+
proxy.type === 'vless' && proxy.network === 'xhttp'
818+
? proxy['xhttp-opts']?.['download-settings']
819+
: undefined;
820+
if (xhttpDownloadSettings?.['shadow-tls-opts']) {
821+
xhttpDownloadSettings.plugin = 'shadow-tls';
822+
xhttpDownloadSettings['plugin-opts'] = {
823+
host: xhttpDownloadSettings.servername,
824+
password: xhttpDownloadSettings['shadow-tls-opts'].password,
825+
version: xhttpDownloadSettings['shadow-tls-opts'].version,
826+
};
827+
delete xhttpDownloadSettings['shadow-tls-opts'];
828+
}
829+
if (
830+
xhttpDownloadSettings?.plugin === 'shadow-tls' &&
831+
xhttpDownloadSettings['plugin-opts']
832+
) {
833+
if (
834+
xhttpDownloadSettings.alpn &&
835+
!xhttpDownloadSettings['plugin-opts'].alpn
836+
) {
837+
xhttpDownloadSettings['plugin-opts'].alpn =
838+
xhttpDownloadSettings.alpn;
839+
}
840+
delete xhttpDownloadSettings.alpn;
841+
}
804842
if (proxy.network === 'ws') {
805843
if (!proxy['ws-opts'] && (proxy['ws-path'] || proxy['ws-headers'])) {
806844
proxy['ws-opts'] = {};

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

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
isPresent,
44
normalizePluginMuxBooleanValue,
55
produceProxyListOutput,
6+
restoreShadowTLSProxyOpts,
67
supportsShadowsocksV2rayPluginMode,
78
} from '@/core/proxy-utils/producers/utils';
89
import { isNotBlank, isPlainObject } from '@/utils';
@@ -76,13 +77,36 @@ export default function ClashMeta_Producer() {
7677
return false;
7778
} else if (
7879
hasMihomoShadowTls(proxy) &&
79-
(!['ss', 'snell'].includes(proxy.type) ||
80+
(![
81+
'ss',
82+
'snell',
83+
'vmess',
84+
'vless',
85+
'trojan',
86+
'anytls',
87+
].includes(proxy.type) ||
8088
!isSupportedMihomoVersion(
8189
getMihomoShadowTlsVersion(proxy),
82-
[1, 2, 3],
90+
['ss', 'snell'].includes(proxy.type)
91+
? [1, 2, 3]
92+
: [0, 1, 2, 3],
8393
))
8494
) {
8595
return false;
96+
} else if (
97+
proxy.type === 'vless' &&
98+
proxy.network === 'xhttp' &&
99+
hasMihomoShadowTls(
100+
proxy['xhttp-opts']?.['download-settings'],
101+
) &&
102+
!isSupportedMihomoVersion(
103+
getMihomoShadowTlsVersion(
104+
proxy['xhttp-opts']['download-settings'],
105+
),
106+
[0, 1, 2, 3],
107+
)
108+
) {
109+
return false;
86110
} else if (hasMihomoSnellShadowTlsObfsConflict(proxy)) {
87111
$.error(
88112
`Platform Mihomo does not support Snell shadow-tls with obfs for proxy ${proxy.name}. Proxy has been filtered.`,
@@ -150,6 +174,8 @@ export default function ClashMeta_Producer() {
150174
.map((proxy) => {
151175
warnMihomoUnsupportedEchDnsFields(proxy, type);
152176

177+
restoreShadowTLSProxyOpts(proxy);
178+
153179
if (proxy['reality-opts'] && !proxy['client-fingerprint']) {
154180
proxy['client-fingerprint'] = 'chrome';
155181
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
isPresent,
44
isShadowsocksOverTls,
55
produceProxyListOutput,
6+
restoreShadowTLSProxyOpts,
67
supportsShadowsocksV2rayPluginMode,
78
} from '@/core/proxy-utils/producers/utils';
89
import {
@@ -58,6 +59,8 @@ export default function Shadowrocket_Producer() {
5859
return true;
5960
})
6061
.map((proxy) => {
62+
restoreShadowTLSProxyOpts(proxy);
63+
6164
if (proxy.type === 'vmess') {
6265
// handle vmess aead
6366
if (isPresent(proxy, 'aead')) {

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,42 @@ export function supportsShadowsocksV2rayPluginMode(proxy, supportedModes) {
7171
return supportedModes.includes(normalizedMode);
7272
}
7373

74+
function restoreShadowTLSOpts(target, serverNameKey) {
75+
if (target?.plugin !== 'shadow-tls' || !target['plugin-opts']) {
76+
return undefined;
77+
}
78+
79+
const opts = target['plugin-opts'];
80+
const enabled =
81+
Boolean(opts.password) ||
82+
(opts.version != null && Number(opts.version) !== 0);
83+
target['shadow-tls-opts'] = {
84+
password: opts.password,
85+
version: opts.version,
86+
};
87+
if (opts.host != null) target[serverNameKey] = opts.host;
88+
if (opts.alpn != null) target.alpn = opts.alpn;
89+
delete target.plugin;
90+
delete target['plugin-opts'];
91+
return enabled;
92+
}
93+
94+
export function restoreShadowTLSProxyOpts(proxy) {
95+
if (['vmess', 'vless', 'trojan', 'anytls'].includes(proxy.type)) {
96+
const restored = restoreShadowTLSOpts(proxy, 'sni');
97+
if (restored && ['vmess', 'vless'].includes(proxy.type)) {
98+
proxy.tls = true;
99+
}
100+
}
101+
102+
if (proxy.type === 'vless' && proxy.network === 'xhttp') {
103+
const downloadSettings = proxy['xhttp-opts']?.['download-settings'];
104+
if (restoreShadowTLSOpts(downloadSettings, 'servername')) {
105+
downloadSettings.tls = true;
106+
}
107+
}
108+
}
109+
74110
function parseWireGuardCIDR(cidr, max) {
75111
if (cidr == null) return undefined;
76112
const normalized = `${cidr}`.trim();

0 commit comments

Comments
 (0)