Skip to content

Commit 58533a6

Browse files
authored
Merge pull request cmliu#912 from cmliu/beta2.0
Beta2.0
2 parents a351d6b + 8350ac1 commit 58533a6

1 file changed

Lines changed: 72 additions & 6 deletions

File tree

_worker.js

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ export default {
279279
return new Response('优选订阅生成器异常:' + error.message, { status: 403 });
280280
}
281281
}
282-
282+
const ECHLINK参数 = config_JSON.ECH ? '&ech=' + encodeURIComponent(await getECH(config_JSON.HOST)) : '';
283283
订阅内容 = 其他节点LINK + 完整优选IP.map(原始地址 => {
284284
// 统一正则: 匹配 域名/IPv4/IPv6地址 + 可选端口 + 可选备注
285285
// 示例:
@@ -301,7 +301,7 @@ export default {
301301
return null;
302302
}
303303

304-
return `${协议类型}://00000000-0000-4000-8000-000000000000@${节点地址}:${节点端口}?security=tls&type=${config_JSON.传输协议}&host=example.com&sni=example.com&path=${encodeURIComponent(config_JSON.随机路径 ? 随机路径() + 节点路径 : 节点路径) + TLS分片参数}&encryption=none${config_JSON.跳过证书验证 ? '&allowInsecure=1' : ''}#${encodeURIComponent(节点备注)}`;
304+
return `${协议类型}://00000000-0000-4000-8000-000000000000@${节点地址}:${节点端口}?security=tls&type=${config_JSON.传输协议 + ECHLINK参数}&host=example.com&fp=${config_JSON.Fingerprint}&sni=example.com&path=${encodeURIComponent(config_JSON.随机路径 ? 随机路径() + 节点路径 : 节点路径) + TLS分片参数}&encryption=none${config_JSON.跳过证书验证 ? '&insecure=1&allowInsecure=1' : ''}#${encodeURIComponent(节点备注)}`;
305305
}).filter(item => item !== null).join('\n');
306306
} else { // 订阅转换
307307
const 订阅转换URL = `${config_JSON.订阅转换配置.SUBAPI}/sub?target=${订阅类型}&url=${encodeURIComponent(url.protocol + '//' + url.host + '/sub?target=mixed&token=' + 订阅TOKEN + (url.searchParams.has('sub') && url.searchParams.get('sub') != '' ? `&sub=${url.searchParams.get('sub')}` : ''))}&config=${encodeURIComponent(config_JSON.订阅转换配置.SUBCONFIG)}&emoji=${config_JSON.订阅转换配置.SUBEMOJI}&scv=${config_JSON.跳过证书验证}`;
@@ -316,12 +316,12 @@ export default {
316316
}
317317
}
318318

319-
if (!ua.includes('subconverter')) 订阅内容 = 批量替换域名(订阅内容.replace(/00000000-0000-4000-8000-000000000000/g, config_JSON.UUID), config_JSON.HOSTS)
319+
if (!ua.includes('subconverter')) 订阅内容 = await 批量替换域名(订阅内容.replace(/00000000-0000-4000-8000-000000000000/g, config_JSON.UUID), config_JSON.HOSTS)
320320

321321
if (订阅类型 === 'mixed' && (!ua.includes('mozilla') || url.searchParams.has('b64') || url.searchParams.has('base64'))) 订阅内容 = btoa(订阅内容);
322322

323323
if (订阅类型 === 'singbox') {
324-
订阅内容 = Singbox订阅配置文件热补丁(订阅内容);
324+
订阅内容 = Singbox订阅配置文件热补丁(订阅内容, config_JSON.UUID, config_JSON.Fingerprint, config_JSON.ECH ? await getECH(host) : null);
325325
responseHeaders["content-type"] = 'application/json; charset=utf-8';
326326
} else if (订阅类型 === 'clash') {
327327
responseHeaders["content-type"] = 'application/x-yaml; charset=utf-8';
@@ -784,7 +784,7 @@ async function httpConnect(targetHost, targetPort, initialData) {
784784
}
785785
}
786786
//////////////////////////////////////////////////功能性函数///////////////////////////////////////////////
787-
function Singbox订阅配置文件热补丁(sb_json_text) {
787+
function Singbox订阅配置文件热补丁(sb_json_text, uuid = null, fingerprint = "chrome", ech_config = null) {
788788
try {
789789
let config = JSON.parse(sb_json_text);
790790

@@ -929,6 +929,35 @@ function Singbox订阅配置文件热补丁(sb_json_text) {
929929
}
930930
});
931931

932+
// --- 4. UUID 匹配节点的 TLS 热补丁 (utls & ech) ---
933+
if (uuid) {
934+
config.outbounds.forEach(outbound => {
935+
// 仅处理包含 uuid 且匹配的节点
936+
if (outbound.uuid && outbound.uuid === uuid) {
937+
// 确保 tls 对象存在
938+
if (!outbound.tls) {
939+
outbound.tls = { enabled: true };
940+
}
941+
942+
// 添加/更新 utls 配置
943+
if (fingerprint) {
944+
outbound.tls.utls = {
945+
enabled: true,
946+
fingerprint: fingerprint
947+
};
948+
}
949+
950+
// 如果提供了 ech_config,添加/更新 ech 配置
951+
if (ech_config) {
952+
outbound.tls.ech = {
953+
enabled: true,
954+
config: `-----BEGIN ECH CONFIGS-----\n${ech_config}\n-----END ECH CONFIGS-----`
955+
};
956+
}
957+
}
958+
});
959+
}
960+
932961
return JSON.stringify(config, null, 2);
933962
} catch (e) {
934963
console.error("Singbox热补丁执行失败:", e);
@@ -1071,6 +1100,38 @@ function 批量替换域名(内容, hosts, 每组数量 = 2) {
10711100
});
10721101
}
10731102

1103+
async function getECH(host) {
1104+
try {
1105+
const res = await fetch(`https://1.1.1.1/dns-query?name=${encodeURIComponent(host)}&type=65`, { headers: { 'accept': 'application/dns-json' } });
1106+
const data = await res.json();
1107+
if (!data.Answer?.length) return '';
1108+
for (let ans of data.Answer) {
1109+
if (ans.type !== 65 || !ans.data) continue;
1110+
const match = ans.data.match(/ech=([^\s]+)/);
1111+
if (match) return match[1].replace(/"/g, '');
1112+
if (ans.data.startsWith('\\#')) {
1113+
const hex = ans.data.split(' ').slice(2).join('');
1114+
const bytes = new Uint8Array(hex.match(/.{1,2}/g).map(b => parseInt(b, 16)));
1115+
let offset = 2;
1116+
while (offset < bytes.length && bytes[offset++] !== 0)
1117+
offset += bytes[offset - 1];
1118+
1119+
while (offset + 4 <= bytes.length) {
1120+
const key = (bytes[offset] << 8) | bytes[offset + 1];
1121+
const len = (bytes[offset + 2] << 8) | bytes[offset + 3];
1122+
offset += 4;
1123+
1124+
if (key === 5) return btoa(String.fromCharCode(...bytes.slice(offset, offset + len)));
1125+
offset += len;
1126+
}
1127+
}
1128+
}
1129+
return '';
1130+
} catch {
1131+
return '';
1132+
}
1133+
}
1134+
10741135
async function 读取config_JSON(env, hostname, userID, path, 重置配置 = false) {
10751136
//const host = 随机替换通配符(hostname);
10761137
const host = hostname;
@@ -1086,6 +1147,8 @@ async function 读取config_JSON(env, hostname, userID, path, 重置配置 = fal
10861147
启用0RTT: false,
10871148
TLS分片: null,
10881149
随机路径: false,
1150+
ECH: false,
1151+
Fingerprint: "chrome",
10891152
优选订阅生成: {
10901153
local: true, // true: 基于本地的优选地址 false: 优选订阅生成器
10911154
本地IP库: {
@@ -1152,7 +1215,10 @@ async function 读取config_JSON(env, hostname, userID, path, 重置配置 = fal
11521215
config_JSON.UUID = userID;
11531216
config_JSON.PATH = path ? (path.startsWith('/') ? path : '/' + path) : (config_JSON.反代.SOCKS5.启用 ? ('/' + config_JSON.反代.SOCKS5.启用 + (config_JSON.反代.SOCKS5.全局 ? '://' : '=') + config_JSON.反代.SOCKS5.账号) : (config_JSON.反代.PROXYIP === 'auto' ? '/' : `/proxyip=${config_JSON.反代.PROXYIP}`));
11541217
const TLS分片参数 = config_JSON.TLS分片 == 'Shadowrocket' ? `&fragment=${encodeURIComponent('1,40-60,30-50,tlshello')}` : config_JSON.TLS分片 == 'Happ' ? `&fragment=${encodeURIComponent('3,1,tlshello')}` : '';
1155-
config_JSON.LINK = `${config_JSON.协议类型}://${userID}@${host}:443?security=tls&type=${config_JSON.传输协议}&host=${host}&sni=${host}&path=${encodeURIComponent(config_JSON.启用0RTT ? config_JSON.PATH + '?ed=2560' : config_JSON.PATH) + TLS分片参数}&encryption=none${config_JSON.跳过证书验证 ? '&allowInsecure=1' : ''}#${encodeURIComponent(config_JSON.优选订阅生成.SUBNAME)}`;
1218+
if (!config_JSON.Fingerprint) config_JSON.Fingerprint = "chrome";
1219+
if (!config_JSON.ECH) config_JSON.ECH = false;
1220+
const ECHLINK参数 = config_JSON.ECH ? '&ech=' + encodeURIComponent(await getECH(config_JSON.HOST)) : '';
1221+
config_JSON.LINK = `${config_JSON.协议类型}://${userID}@${host}:443?security=tls&type=${config_JSON.传输协议 + ECHLINK参数}&host=${host}&fp=${config_JSON.Fingerprint}&sni=${host}&path=${encodeURIComponent(config_JSON.启用0RTT ? config_JSON.PATH + '?ed=2560' : config_JSON.PATH) + TLS分片参数}&encryption=none${config_JSON.跳过证书验证 ? '&insecure=1&allowInsecure=1' : ''}#${encodeURIComponent(config_JSON.优选订阅生成.SUBNAME)}`;
11561222
config_JSON.优选订阅生成.TOKEN = await MD5MD5(hostname + userID);
11571223

11581224
const 初始化TG_JSON = { BotToken: null, ChatID: null };

0 commit comments

Comments
 (0)