Skip to content

Commit b8d94e4

Browse files
authored
Merge pull request cmliu#872 from cmliu/beta2.0
Beta2.0
2 parents e198afb + b5efe79 commit b8d94e4

2 files changed

Lines changed: 72 additions & 49 deletions

File tree

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,11 @@
118118
|--------|---------|-|-----|
119119
| ADMIN | `123456` || 面板登录密码 |
120120
| KEY | `CMLiussss` || 快速订阅密钥,访问`/CMLiussss`即可快读订阅。 |
121-
| HOST | `edt-pages.github.io` || 强制固定伪装域名 |
121+
| HOST | `edt-pages.github.io` || 强制固定伪装域名(支持多元素,使用`换行符``,`做间隔) |
122122
| UUID | `90cd4a77-141a-43c9-991b-08263cfe9c10` || 强制固定UUID |
123123
| PATH | `/` || 强制固定路径 |
124124
| PROXYIP | `proxyip.cmliussss.net:443` || 更换默认内置PROXYIP |
125-
| URL | `https://blog.cmliussss.com` || 主页反代伪装(乱设容易触发反诈,反代被墙的网站会加速域名被墙) |
125+
| URL | `https://cloudflare-error-page-3th.pages.dev` || 主页反代伪装(乱设容易触发反诈,反代被墙的网站会加速域名被墙) |
126126
| GO2SOCKS5 | `blog.cmliussss.com`,`*.ip111.cn`,`*google.com` || 设置`SOCKS5``HTTP`变量之后,可设置强制使用socks5访问名单(设置为`*`可作为全局代理) |
127127

128128
## 🔧 实用技巧
@@ -184,4 +184,5 @@
184184
- [白嫖哥](https://t.me/bestcfipas)
185185
- [Mingyu](https://github.com/ymyuuu/workers-vless)
186186
- [Alexandre Kojève](https://t.me/Enkelte_notif/784):stallTCP v1.3
187-
- [eooce](https://github.com/eooce/Cloudflare-proxy)
187+
- [eooce](https://github.com/eooce/Cloudflare-proxy)
188+
- [Sukka](https://ip.skk.moe/)

_worker.js

Lines changed: 68 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@ export default {
88
const url = new URL(request.url);
99
const UA = request.headers.get('User-Agent') || 'null';
1010
const upgradeHeader = request.headers.get('Upgrade');
11-
const 管理员密码 = env.ADMIN || env.admin || env.PASSWORD || env.password || env.pswd || env.TOKEN || env.KEY;
11+
const 管理员密码 = env.ADMIN || env.admin || env.PASSWORD || env.password || env.pswd || env.TOKEN || env.KEY || env.UUID || env.uuid;
1212
const 加密秘钥 = env.KEY || '勿动此默认密钥,有需求请自行通过添加变量KEY进行修改';
1313
const userIDMD5 = await MD5MD5(管理员密码 + 加密秘钥);
1414
const uuidRegex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
1515
const envUUID = env.UUID || env.uuid;
1616
const userID = (envUUID && uuidRegex.test(envUUID)) ? envUUID.toLowerCase() : [userIDMD5.slice(0, 8), userIDMD5.slice(8, 12), '4' + userIDMD5.slice(13, 16), '8' + userIDMD5.slice(17, 20), userIDMD5.slice(20)].join('-');
17-
const host = env.HOST ? env.HOST.toLowerCase().replace(/^https?:\/\//, '').split('/')[0].split(':')[0] : url.hostname;
17+
const hosts = env.HOST ? (await 整理成数组(env.HOST)).map(h => h.toLowerCase().replace(/^https?:\/\//, '').split('/')[0].split(':')[0]) : [url.hostname];
18+
const host = hosts[0];
1819
if (env.PROXYIP) {
1920
const proxyIPs = await 整理成数组(env.PROXYIP);
2021
反代IP = proxyIPs[Math.floor(Math.random() * proxyIPs.length)];
@@ -229,53 +230,68 @@ export default {
229230
if (订阅类型 === 'mixed') {
230231
const 节点路径 = config_JSON.启用0RTT ? config_JSON.PATH + '?ed=2560' : config_JSON.PATH;
231232
const TLS分片参数 = config_JSON.TLS分片 == 'Shadowrocket' ? `&fragment=${encodeURIComponent('1,40-60,30-50,tlshello')}` : config_JSON.TLS分片 == 'Happ' ? `&fragment=${encodeURIComponent('3,1,tlshello')}` : '';
232-
const 完整优选列表 = config_JSON.优选订阅生成.本地IP库.随机IP ? (await 生成随机IP(request, config_JSON.优选订阅生成.本地IP库.随机数量, config_JSON.优选订阅生成.本地IP库.指定端口))[0] : await env.KV.get('ADD.txt') ? await 整理成数组(await env.KV.get('ADD.txt')) : (await 生成随机IP(request, config_JSON.优选订阅生成.本地IP库.随机数量, config_JSON.优选订阅生成.本地IP库.指定端口))[0];
233-
const 优选API = [], 优选IP = [], 其他节点 = [];
234-
for (const 元素 of 完整优选列表) {
235-
if (元素.toLowerCase().startsWith('https://')) 优选API.push(元素);
236-
else if (元素.toLowerCase().includes('://')) 其他节点.push(元素);
237-
else 优选IP.push(元素);
238-
}
239-
const 其他节点LINK = 其他节点.join('\n') + '\n';
233+
let 完整优选IP = [], 其他节点LINK = '';
234+
240235
if (!url.searchParams.has('sub') && config_JSON.优选订阅生成.local) { // 本地生成订阅
236+
const 完整优选列表 = config_JSON.优选订阅生成.本地IP库.随机IP ? (await 生成随机IP(request, config_JSON.优选订阅生成.本地IP库.随机数量, config_JSON.优选订阅生成.本地IP库.指定端口))[0] : await env.KV.get('ADD.txt') ? await 整理成数组(await env.KV.get('ADD.txt')) : (await 生成随机IP(request, config_JSON.优选订阅生成.本地IP库.随机数量, config_JSON.优选订阅生成.本地IP库.指定端口))[0];
237+
const 优选API = [], 优选IP = [], 其他节点 = [];
238+
for (const 元素 of 完整优选列表) {
239+
if (元素.toLowerCase().startsWith('https://')) 优选API.push(元素);
240+
else if (元素.toLowerCase().includes('://')) 其他节点.push(元素);
241+
else 优选IP.push(元素);
242+
}
243+
其他节点LINK = 其他节点.join('\n') + '\n';
241244
const 优选API的IP = await 请求优选API(优选API);
242-
const 完整优选IP = [...new Set(优选IP.concat(优选API的IP))];
243-
订阅内容 = 完整优选IP.map(原始地址 => {
244-
// 统一正则: 匹配 域名/IPv4/IPv6地址 + 可选端口 + 可选备注
245-
// 示例:
246-
// - 域名: hj.xmm1993.top:2096#备注 或 example.com
247-
// - IPv4: 166.0.188.128:443#Los Angeles 或 166.0.188.128
248-
// - IPv6: [2606:4700::]:443#CMCC 或 [2606:4700::]
249-
const regex = /^(\[[\da-fA-F:]+\]|[\d.]+|[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)*)(?::(\d+))?(?:#(.+))?$/;
250-
const match = 原始地址.match(regex);
251-
252-
let 节点地址, 节点端口 = "443", 节点备注;
253-
254-
if (match) {
255-
节点地址 = match[1]; // IP地址或域名(可能带方括号)
256-
节点端口 = match[2] || "443"; // 端口,默认443
257-
节点备注 = match[3] || 节点地址; // 备注,默认为地址本身
258-
} else {
259-
// 不规范的格式,跳过处理返回null
260-
console.warn(`[订阅内容] 不规范的IP格式已忽略: ${原始地址}`);
261-
return null;
262-
}
263-
const 节点HOST = 随机替换通配符(host);
264-
return `${协议类型}://${config_JSON.UUID}@${节点地址}:${节点端口}?security=tls&type=${config_JSON.传输协议}&host=${节点HOST}&sni=${节点HOST}&path=${encodeURIComponent(config_JSON.随机路径 ? 随机路径() + 节点路径 : 节点路径) + TLS分片参数}&encryption=none${config_JSON.跳过证书验证 ? '&allowInsecure=1' : ''}#${encodeURIComponent(节点备注)}`;
265-
}).filter(item => item !== null).join('\n');
266-
订阅内容 = btoa(其他节点LINK + 订阅内容);
245+
完整优选IP = [...new Set(优选IP.concat(优选API的IP))];
267246
} else { // 优选订阅生成器
268247
let 优选订阅生成器HOST = url.searchParams.get('sub') || config_JSON.优选订阅生成.SUB;
269248
优选订阅生成器HOST = 优选订阅生成器HOST && !/^https?:\/\//i.test(优选订阅生成器HOST) ? `https://${优选订阅生成器HOST}` : 优选订阅生成器HOST;
270-
const 优选订阅生成器URL = `${优选订阅生成器HOST}/sub?host=example.com&${协议类型 === ('v' + 'le' + 'ss') ? 'uuid' : 'pw'}=00000000-0000-4000-8000-000000000000&path=${encodeURIComponent(config_JSON.随机路径 ? 随机路径() + 节点路径 : 节点路径) + TLS分片参数}&type=${config_JSON.传输协议}`;
249+
const 优选订阅生成器URL = `${优选订阅生成器HOST}/sub?host=example.com&uuid=00000000-0000-4000-8000-000000000000`;
271250
try {
272251
const response = await fetch(优选订阅生成器URL, { headers: { 'User-Agent': 'v2rayN/edge' + 'tunnel (https://github.com/cmliu/edge' + 'tunnel)' } });
273-
if (response.ok) 订阅内容 = btoa(其他节点LINK + atob(await response.text()));
274-
else return new Response('优选订阅生成器异常:' + response.statusText, { status: response.status });
252+
if (!response.ok) return new Response('优选订阅生成器异常:' + response.statusText, { status: response.status });
253+
const 优选订阅生成器返回订阅内容 = atob(await response.text());
254+
const 订阅行列表 = 优选订阅生成器返回订阅内容.includes('\r\n') ? 优选订阅生成器返回订阅内容.split('\r\n') : 优选订阅生成器返回订阅内容.split('\n');
255+
for (const 行内容 of 订阅行列表) {
256+
if (!行内容.trim()) continue; // 跳过空行
257+
if (行内容.includes('00000000-0000-4000-8000-000000000000') && 行内容.includes('example.com')) { // 这是优选IP行,提取 域名:端口#备注
258+
const 地址匹配 = 行内容.match(/:\/\/[^@]+@([^?]+)/);
259+
if (地址匹配) {
260+
let 地址端口 = 地址匹配[1], 备注 = ''; // 域名:端口 或 IP:端口
261+
const 备注匹配 = 行内容.match(/#(.+)$/);
262+
if (备注匹配) 备注 = '#' + decodeURIComponent(备注匹配[1]);
263+
完整优选IP.push(地址端口 + 备注);
264+
}
265+
} else 其他节点LINK += 行内容 + '\n';
266+
}
275267
} catch (error) {
276268
return new Response('优选订阅生成器异常:' + error.message, { status: 403 });
277269
}
278270
}
271+
272+
订阅内容 = 完整优选IP.map(原始地址 => {
273+
// 统一正则: 匹配 域名/IPv4/IPv6地址 + 可选端口 + 可选备注
274+
// 示例:
275+
// - 域名: hj.xmm1993.top:2096#备注 或 example.com
276+
// - IPv4: 166.0.188.128:443#Los Angeles 或 166.0.188.128
277+
// - IPv6: [2606:4700::]:443#CMCC 或 [2606:4700::]
278+
const regex = /^(\[[\da-fA-F:]+\]|[\d.]+|[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)*)(?::(\d+))?(?:#(.+))?$/;
279+
const match = 原始地址.match(regex);
280+
281+
let 节点地址, 节点端口 = "443", 节点备注;
282+
283+
if (match) {
284+
节点地址 = match[1]; // IP地址或域名(可能带方括号)
285+
节点端口 = match[2] || "443"; // 端口,默认443
286+
节点备注 = match[3] || 节点地址; // 备注,默认为地址本身
287+
} else {
288+
// 不规范的格式,跳过处理返回null
289+
console.warn(`[订阅内容] 不规范的IP格式已忽略: ${原始地址}`);
290+
return null;
291+
}
292+
293+
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(节点备注)}`;
294+
}).filter(item => item !== null).join('\n');
279295
} else { // 订阅转换
280296
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.跳过证书验证}`;
281297
try {
@@ -288,10 +304,11 @@ export default {
288304
return new Response('订阅转换后端异常:' + error.message, { status: 403 });
289305
}
290306
}
291-
if (订阅类型 === 'mixed') {
292-
订阅内容 = 批量替换域名(atob(订阅内容).replace(/00000000-0000-4000-8000-000000000000/g, config_JSON.UUID), host);
293-
if (!ua.includes('mozilla')) 订阅内容 = btoa(订阅内容);
294-
} else 订阅内容 = 批量替换域名(订阅内容.replace(/00000000-0000-4000-8000-000000000000/g, config_JSON.UUID), host);
307+
308+
if (!ua.includes('subconverter')) 订阅内容 = 批量替换域名(订阅内容.replace(/00000000-0000-4000-8000-000000000000/g, config_JSON.UUID), config_JSON.HOSTS)
309+
310+
if (!ua.includes('mozilla') && 订阅类型 === 'mixed') 订阅内容 = btoa(订阅内容);
311+
295312
if (订阅类型 === 'singbox') {
296313
订阅内容 = JSON.stringify(JSON.parse(订阅内容), null, 2);
297314
responseHeaders["content-type"] = 'application/json; charset=utf-8';
@@ -502,7 +519,8 @@ async function forwardataTCP(host, portNum, rawData, ws, respHeader, remoteConnW
502519
connectStreams(newSocket, ws, respHeader, null);
503520
}
504521

505-
if (启用SOCKS5反代 && 启用SOCKS5全局反代) {
522+
const 验证SOCKS5白名单 = (addr) => SOCKS5白名单.some(p => new RegExp(`^${p.replace(/\*/g, '.*')}$`, 'i').test(addr));
523+
if (启用SOCKS5反代 && (启用SOCKS5全局反代 || 验证SOCKS5白名单(host))) {
506524
try {
507525
await connecttoPry();
508526
} catch (err) {
@@ -831,21 +849,23 @@ function 随机替换通配符(h) {
831849
});
832850
}
833851

834-
function 批量替换域名(内容, host, 每组数量 = 2) {
852+
function 批量替换域名(内容, hosts, 每组数量 = 2) {
835853
let count = 0, currentRandomHost = null;
836854
return 内容.replace(/example\.com/g, () => {
837-
if (count % 每组数量 === 0) currentRandomHost = 随机替换通配符(host);
855+
if (count % 每组数量 === 0) currentRandomHost = 随机替换通配符(hosts[Math.floor(Math.random() * hosts.length)]);
838856
count++;
839857
return currentRandomHost;
840858
});
841859
}
842860

843861
async function 读取config_JSON(env, hostname, userID, path, 重置配置 = false) {
844-
const host = 随机替换通配符(hostname);
862+
//const host = 随机替换通配符(hostname);
863+
const host = hostname;
845864
const 初始化开始时间 = performance.now();
846865
const 默认配置JSON = {
847866
TIME: new Date().toISOString(),
848867
HOST: host,
868+
HOSTS: [hostname],
849869
UUID: userID,
850870
协议类型: "v" + "le" + "ss",
851871
传输协议: "ws",
@@ -912,6 +932,8 @@ async function 读取config_JSON(env, hostname, userID, path, 重置配置 = fal
912932
}
913933

914934
config_JSON.HOST = host;
935+
if (!config_JSON.HOSTS) config_JSON.HOSTS = [hostname];
936+
if (env.HOST) config_JSON.HOSTS = (await 整理成数组(env.HOST)).map(h => h.toLowerCase().replace(/^https?:\/\//, '').split('/')[0].split(':')[0]);
915937
config_JSON.UUID = userID;
916938
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}`));
917939
const TLS分片参数 = config_JSON.TLS分片 == 'Shadowrocket' ? `&fragment=${encodeURIComponent('1,40-60,30-50,tlshello')}` : config_JSON.TLS分片 == 'Happ' ? `&fragment=${encodeURIComponent('3,1,tlshello')}` : '';

0 commit comments

Comments
 (0)