diff --git a/README.md b/README.md
index effc2c7..53a69eb 100644
--- a/README.md
+++ b/README.md
@@ -136,7 +136,10 @@ Telegram交流群:[@CMLiussss](https://t.me/CMLiussss),**感谢[Alice Networ
| 变量名 | 示例 | 必填 | 备注 | YT |
|--------|---------|-|-----|-----|
| UUID | `90cd4a77-141a-43c9-991b-08263cfe9c10` |✅| Powershell -NoExit -Command "[guid]::NewGuid()"| [Video](https://www.youtube.com/watch?v=s91zjpw3-P8&t=72s) |
-| PROXYIP | `proxyip.fxxk.dedyn.io:443` |❌| 备选作为访问CFCDN站点的代理节点(支持多ProxyIP, ProxyIP之间使用`,`或`换行`作间隔) | [Video](https://www.youtube.com/watch?v=s91zjpw3-P8&t=166s) |
+| KEY | `token` |❌| 动态UUID秘钥,使用变量`KEY`的时候,将不再启用变量`UUID`| |
+| TIME | `7` |❌| 动态UUID有效时间(单位:天)| |
+| UPTIME | `3` |❌| 动态UUID有效时间(默认:北京时间`3`点更新) | |
+| PROXYIP | `proxyip.fxxk.dedyn.io:443` |❌| 备选作为访问CFCDN站点的代理节点(支持自定义ProxyIP端口, 支持多ProxyIP, ProxyIP之间使用`,`或`换行`作间隔) | [Video](https://www.youtube.com/watch?v=s91zjpw3-P8&t=166s) |
| SOCKS5 | `user:password@127.0.0.1:1080` |❌| 优先作为访问CFCDN站点的SOCKS5代理(支持多socks5, socks5之间使用`,`或`换行`作间隔) | [Video](https://www.youtube.com/watch?v=s91zjpw3-P8&t=826s) |
| GO2SOCKS5 | `blog.cmliussss.com`,`*.ip111.cn`,`*google.com` |❌| 设置`SOCKS5`变量之后,可设置强制使用socks5访问名单(`*`可作为通配符,`换行`作多元素间隔) ||
| ADD | `icook.tw:2053#官方优选域名` |❌| 本地优选TLS域名/优选IP(支持多元素之间`,`或`换行`作间隔) ||
@@ -158,6 +161,12 @@ Telegram交流群:[@CMLiussss](https://t.me/CMLiussss),**感谢[Alice Networ
| CFKEY | `c6a944b5c956b6c18c2352880952bced8b85e` |❌| CF账户Global API Key(与`CFEMAIL`都填上后, 订阅信息将显示请求使用量, 小白别用) | |
| CFPORTS | `2053`,`2096`,`8443` |❌| CF账户标准端口列表 | |
+**注意: 填入`KEY`后将不再启用`UUID`!请二选一使用!!!**
+1. 填入`KEY`后,永久订阅地址为`https://[YOUR-URL]/[YOUR-KEY]`;
+2. 填入`KEY`后,临时订阅地址为`https://[YOUR-URL]/[YOUR-UUID]`;
+3. **动态UUID**的订阅使用时间为**1**个`TIME`有效时间周期;
+4. **动态UUID**的节点使用时间为**2**个`TIME`有效时间周期(也就是动态UUID失效的了,节点也可继续使用一个周期,只是无法继续更新订阅);
+
**注意: 填入`SOCKS5`后将不再启用`PROXYIP`!请二选一使用!!!**
**注意: 填入`SUB`后将不再启用`ADD*`类变量生成的订阅内容!请二选一使用!!!**
diff --git a/_worker.js b/_worker.js
index 9312dd8..5f5e5de 100644
--- a/_worker.js
+++ b/_worker.js
@@ -77,6 +77,9 @@ let proxyhosts = [];//本地代理域名池
let proxyhostsURL = 'https://raw.githubusercontent.com/cmliu/CFcdnVmess2sub/main/proxyhosts';//在线代理域名池URL
let RproxyIP = 'false';
let httpsPorts = ["2053","2083","2087","2096","8443"];
+let effectiveTime = 7;//有效时间 单位:天
+let updateTime = 3;//更新时间
+let userIDLow;
export default {
/**
* @param {import("@cloudflare/workers-types").Request} request
@@ -97,7 +100,14 @@ export default {
fakeUserID = fakeUserIDMD5.slice(0, 8) + "-" + fakeUserIDMD5.slice(8, 12) + "-" + fakeUserIDMD5.slice(12, 16) + "-" + fakeUserIDMD5.slice(16, 20) + "-" + fakeUserIDMD5.slice(20);
fakeHostName = fakeUserIDMD5.slice(6, 9) + "." + fakeUserIDMD5.slice(13, 19);
//console.log(`虚假UUID: ${fakeUserID}`); // 打印fakeID
-
+ if (env.KEY) {
+ const userIDs = await generateDynamicUUID(env.KEY);
+ userID = userIDs[0];
+ userIDLow = userIDs[1];
+ //console.log(`启用动态UUID\n秘钥KEY: ${env.KEY}\nUUIDNow: ${userID}\nUUIDLow: ${userIDLow}`);
+ effectiveTime = env.TIME || effectiveTime;
+ updateTime = env.UPTIME || updateTime;
+ }
proxyIP = env.PROXYIP || proxyIP;
proxyIPs = await ADD(proxyIP);
proxyIP = proxyIPs[Math.floor(Math.random() * proxyIPs.length)];
@@ -146,9 +156,8 @@ export default {
FileName = env.SUBNAME || FileName;
if (url.searchParams.has('notls')) noTLS = 'true';
if (!upgradeHeader || upgradeHeader !== 'websocket') {
- // const url = new URL(request.url);
- switch (url.pathname.toLowerCase()) {
- case '/':
+ const 路径 = url.pathname.toLowerCase();
+ if (路径 == '/') {
if (env.URL302) return Response.redirect(env.URL302, 302);
else if (env.URL) return await proxyURL(env.URL, url);
else return new Response(JSON.stringify(request.cf, null, 4), {
@@ -157,12 +166,12 @@ export default {
'content-type': 'application/json',
},
});
- case `/${fakeUserID}`:
- const fakeConfig = await getVLESSConfig(userID, request.headers.get('Host'), sub, 'CF-Workers-SUB', RproxyIP, url);
+ } else if (路径 == `/${fakeUserID}`) {
+ const fakeConfig = await getVLESSConfig(userID, request.headers.get('Host'), sub, 'CF-Workers-SUB', RproxyIP, url, env);
return new Response(`${fakeConfig}`, { status: 200 });
- case `/${userID}`: {
+ } else if (路径 == `/${env.KEY}` || 路径 == `/${userID}`) {
await sendMessage(`#获取订阅 ${FileName}`, request.headers.get('CF-Connecting-IP'), `UA: ${UA}\n域名: ${url.hostname}\n入口: ${url.pathname + url.search}`);
- const vlessConfig = await getVLESSConfig(userID, request.headers.get('Host'), sub, UA, RproxyIP, url);
+ const vlessConfig = await getVLESSConfig(userID, request.headers.get('Host'), sub, UA, RproxyIP, url, env);
const now = Date.now();
//const timestamp = Math.floor(now / 1000);
const today = new Date(now);
@@ -208,8 +217,7 @@ export default {
}
});
}
- }
- default:
+ } else {
if (env.URL302) return Response.redirect(env.URL302, 302);
else if (env.URL) return await proxyURL(env.URL, url);
else return new Response('不用怀疑!你UUID就是错的!!!', { status: 404 });
@@ -243,6 +251,7 @@ export default {
} else {
enableSocks = false;
}
+
return await vlessOverWSHandler(request);
}
} catch (err) {
@@ -419,7 +428,7 @@ async function handleTCPOutBound(remoteSocket, addressType, addressRemote, portR
} else {
// 否则,尝试使用预设的代理 IP(如果有)或原始地址重试连接
if (!proxyIP || proxyIP == '') {
- proxyIP = atob('cHJveHlpcC5meHhrLmRlZHluLmlv');
+ proxyIP = atob('cHJveHlpcC50cDEuY21saXVzc3NzLmNvbQ==');
} else if (proxyIP.includes(']:')) {
portRemote = proxyIP.split(']:')[1] || portRemote;
proxyIP = proxyIP.split(']:')[0] || proxyIP;
@@ -427,6 +436,7 @@ async function handleTCPOutBound(remoteSocket, addressType, addressRemote, portR
portRemote = proxyIP.split(':')[1] || portRemote;
proxyIP = proxyIP.split(':')[0] || proxyIP;
}
+ if (proxyIP.includes('.tp')) portRemote = proxyIP.split('.tp')[1].split('.')[0] || portRemote;
tcpSocket = await connectAndWrite(proxyIP || addressRemote, portRemote);
}
// 无论重试是否成功,都要关闭 WebSocket(可能是为了重新建立连接)
@@ -560,9 +570,15 @@ function processVlessHeader(vlessBuffer, userID) {
let isUDP = false;
// 验证用户 ID(接下来的 16 个字节)
- if (stringify(new Uint8Array(vlessBuffer.slice(1, 17))) === userID) {
- isValidUser = true;
+ function isUserIDValid(userID, userIDLow, buffer) {
+ const userIDArray = new Uint8Array(buffer.slice(1, 17));
+ const userIDString = stringify(userIDArray);
+ return userIDString === userID || userIDString === userIDLow;
}
+
+ // 使用函数验证
+ isValidUser = isUserIDValid(userID, userIDLow, vlessBuffer);
+
// 如果用户 ID 无效,返回错误
if (!isValidUser) {
return {
@@ -1251,17 +1267,18 @@ function checkSUB(host) {
if ((!sub || sub == '') && (addresses.length + addressesapi.length + addressesnotls.length + addressesnotlsapi.length + addressescsv.length) == 0){
addresses = [
'Join.my.Telegram.channel.CMLiussss.to.unlock.more.premium.nodes.cf.090227.xyz#加入我的频道t.me/CMLiussss解锁更多优选节点',
+ '127.0.0.1:1234#CFnat',
'visa.cn:443',
- 'www.visa.com:8443',
- 'cis.visa.com:2053',
- 'africa.visa.com:2083',
- 'www.visa.com.sg:2087',
- 'www.visaeurope.at:2096',
- 'www.visa.com.mt:8443',
- 'qa.visamiddleeast.com',
+ 'singapore.com:8443',
+ 'japan.com:2053',
+ 'brazil.com:2083',
+ 'russia.com:2087',
+ 'www.gov.ua:2096',
+ 'www.gco.gov.qa:8443',
+ 'www.gov.se',
'time.is',
'www.wto.org:8443',
- 'chatgpt.com:2087',
+ 'fbi.gov:2087',
'icook.hk',
//'104.17.0.0#IPv4',
'[2606:4700::]#IPv6'
@@ -1269,9 +1286,9 @@ function checkSUB(host) {
if (host.includes(".workers.dev")) addressesnotls = [
'usa.visa.com:2095',
'myanmar.visa.com:8080',
- 'www.visa.com.tw:8880',
+ 'dynadot.com:8880',
'www.visaeurope.ch:2052',
- 'www.visa.com.br:2082',
+ 'shopify.com:2082',
'www.visasoutheasteurope.com:2086'
];
}
@@ -1329,7 +1346,8 @@ let subParams = ['sub','base64','b64','clash','singbox','sb'];
* @param {string} UA
* @returns {Promise}
*/
-async function getVLESSConfig(userID, hostName, sub, UA, RproxyIP, _url) {
+async function getVLESSConfig(userID, hostName, sub, UA, RproxyIP, _url, env) {
+ const uuid = (_url.pathname == `/${env.KEY}`) ? env.KEY : userID;
checkSUB(hostName);
const userAgent = UA.toLowerCase();
const Config = 配置信息(userID , hostName);
@@ -1373,7 +1391,7 @@ async function getVLESSConfig(userID, hostName, sub, UA, RproxyIP, _url) {
else socks5List += `\n ${go2Socks5s.join('\n ')}\n`;
}
- let 订阅器 = '';
+ let 订阅器 = '\n';
if (!sub || sub == '') {
if (enableSocks) 订阅器 += `CFCDN(访问方式): Socks5\n ${newSocks5s.join('\n ')}\n${socks5List}`;
else if (proxyIP && proxyIP != '') 订阅器 += `CFCDN(访问方式): ProxyIP\n ${proxyIPs.join('\n ')}\n`;
@@ -1392,36 +1410,36 @@ async function getVLESSConfig(userID, hostName, sub, UA, RproxyIP, _url) {
订阅器 += `\nSUB(优选订阅生成器): ${sub}`;
}
+ if (env.KEY && _url.pathname !== `/${env.KEY}`) 订阅器 = '';
+ else 订阅器 += `\nSUBAPI(订阅转换后端): ${subProtocol}://${subconverter}\nSUBCONFIG(订阅转换配置文件): ${subconfig}`;
+ const 动态UUID = (uuid != userID) ? `TOKEN: ${uuid}\nUUIDNow: ${userID}\nUUIDLow: ${userIDLow}\nTIME(动态UUID有效时间): ${effectiveTime} 天\nUPTIME(动态UUID更新时间): ${updateTime} 时(北京时间)\n\n` : "";
return `
################################################################
Subscribe / sub 订阅地址, 支持 Base64、clash-meta、sing-box 订阅格式
---------------------------------------------------------------
快速自适应订阅地址:
-https://${proxyhost}${hostName}/${userID}
-https://${proxyhost}${hostName}/${userID}?sub
+https://${proxyhost}${hostName}/${uuid}
+https://${proxyhost}${hostName}/${uuid}?sub
Base64订阅地址:
-https://${proxyhost}${hostName}/${userID}?b64
-https://${proxyhost}${hostName}/${userID}?base64
+https://${proxyhost}${hostName}/${uuid}?b64
+https://${proxyhost}${hostName}/${uuid}?base64
clash订阅地址:
-https://${proxyhost}${hostName}/${userID}?clash
+https://${proxyhost}${hostName}/${uuid}?clash
singbox订阅地址:
-https://${proxyhost}${hostName}/${userID}?sb
-https://${proxyhost}${hostName}/${userID}?singbox
+https://${proxyhost}${hostName}/${uuid}?sb
+https://${proxyhost}${hostName}/${uuid}?singbox
---------------------------------------------------------------
################################################################
${FileName} 配置信息
---------------------------------------------------------------
-HOST: ${hostName}
+${动态UUID}HOST: ${hostName}
UUID: ${userID}
FKID: ${fakeUserID}
UA: ${UA}
-
${订阅器}
-SUBAPI(订阅转换后端): ${subProtocol}://${subconverter}
-SUBCONFIG(订阅转换配置文件): ${subconfig}
---------------------------------------------------------------
################################################################
v2ray
@@ -1903,4 +1921,44 @@ async function sendMessage(type, ip, add_data = "") {
function isValidIPv4(address) {
const ipv4Regex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
return ipv4Regex.test(address);
+}
+
+function generateDynamicUUID(key) {
+ // 获取当前时间是当年的第几周(以北京时间凌晨3点为界)
+ function getWeekOfYear() {
+ const now = new Date();
+ const timezoneOffset = 8; // 北京时间相对于UTC的时区偏移+8小时
+ const adjustedNow = new Date(now.getTime() + timezoneOffset * 60 * 60 * 1000);
+ const start = new Date(2007, 6, 7, 3, 0, 0); // 固定起始日期为2007年7月7日的凌晨3点
+
+ // 计算当前时间与今年1月1日凌晨3点的差距
+ const diff = adjustedNow - start;
+
+ // 一周的毫秒数(7天)
+ const oneWeek = 1000 * 60 * 60 * 24 * effectiveTime;
+
+ // 返回当前是第几周,向上取整
+ return [Math.ceil(diff / oneWeek), Math.ceil((diff / oneWeek)- 1)];
+ }
+
+ const passwdTimes = getWeekOfYear(); // 获取当前周数并存储
+ const passwdTime = passwdTimes[0];
+ const passwdTimeLow = passwdTimes[1];
+
+ // 生成 UUID 的辅助函数
+ function generateUUID(baseString) {
+ const hashBuffer = new TextEncoder().encode(baseString);
+ return crypto.subtle.digest('SHA-256', hashBuffer).then((hash) => {
+ const hashArray = Array.from(new Uint8Array(hash));
+ const hexHash = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
+ let uuid = hexHash.substr(0, 8) + '-' + hexHash.substr(8, 4) + '-4' + hexHash.substr(13, 3) + '-' + (parseInt(hexHash.substr(16, 2), 16) & 0x3f | 0x80).toString(16) + hexHash.substr(18, 2) + '-' + hexHash.substr(20, 12);
+ return uuid;
+ });
+ }
+
+ // 生成两个 UUID
+ const currentUUIDPromise = generateUUID(key + passwdTime);
+ const previousUUIDPromise = generateUUID(key + passwdTimeLow);
+
+ return Promise.all([currentUUIDPromise, previousUUIDPromise]);
}
\ No newline at end of file