支持动态UUID

This commit is contained in:
cmliu
2024-10-20 21:43:59 +08:00
parent 7c9ab8790a
commit 53ed851b9f
2 changed files with 104 additions and 37 deletions

View File

@@ -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*`类变量生成的订阅内容!请二选一使用!!!**

View File

@@ -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}</tg-spoiler>\n域名: ${url.hostname}\n<tg-spoiler>入口: ${url.pathname + url.search}</tg-spoiler>`);
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<string>}
*/
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]);
}