From 8d294e82bf720ee650399d3ae09050599b80e736 Mon Sep 17 00:00:00 2001 From: cmliu Date: Thu, 28 Nov 2024 16:50:34 +0800 Subject: [PATCH] test --- .github/workflows/obfuscator.yml | 8 +- _worker.src.js | 3739 +++++++++++++++--------------- 2 files changed, 1834 insertions(+), 1913 deletions(-) diff --git a/.github/workflows/obfuscator.yml b/.github/workflows/obfuscator.yml index 11e095d3..2e9ae99a 100644 --- a/.github/workflows/obfuscator.yml +++ b/.github/workflows/obfuscator.yml @@ -32,20 +32,22 @@ jobs: line_number=$(awk '/Obfuscate-cmliu/ {print NR; exit}' _worker.src.js) echo "Obfuscate 分段处理行号为: $line_number" head -n $line_number _worker.src.js > src.js - tail -n +$((line_number + 1)) _worker.src.js > obf.js + tail -n +$((line_number + 1)) _worker.src.js > index.js else echo "未检测到 Obfuscate 执行全文件混淆" - cp _worker.src.js obf.js + cp _worker.src.js index.js fi - name: Obfuscate code run: | - javascript-obfuscator obf.js --output _worker.js \ + javascript-obfuscator index.js --output _worker.js \ --compact true \ --control-flow-flattening true \ --control-flow-flattening-threshold 1 \ --dead-code-injection true \ --dead-code-injection-threshold 1 \ + --identifier-names-generator hexadecimal \ + --rename-globals true \ --string-array true \ --string-array-encoding 'rc4' \ --string-array-threshold 1 \ diff --git a/_worker.src.js b/_worker.src.js index c04cfdb5..41284ab3 100644 --- a/_worker.src.js +++ b/_worker.src.js @@ -1,1910 +1,1829 @@ -// _worker.src.js -import { connect } from "cloudflare:sockets"; -let password = 'auto'; -let proxyIP = ''; -// The user name and password do not contain special characters -// Setting the address will ignore proxyIP -// Example: user:pass@host:port or host:port -let socks5Address = ''; - -let addresses = [ - //当sub为空时启用本地优选域名/优选IP,若不带端口号 TLS默认端口为443,#号后为备注别名 - /* - 'Join.my.Telegram.channel.CMLiussss.to.unlock.more.premium.nodes.cf.090227.xyz#加入我的频道t.me/CMLiussss解锁更多优选节点', - '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', - 'time.is', - 'www.wto.org:8443', - 'chatgpt.com:2087', - 'icook.hk', - '104.17.0.0#IPv4', - '[2606:4700::]#IPv6' - */ -]; - -let sub = ''; -let subconverter = 'SUBAPI.fxxk.dedyn.io';// clash订阅转换后端,目前使用CM的订阅转换功能。自带虚假节点信息防泄露 -let subconfig = "https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online_Mini_MultiMode.ini"; //订阅配置文件 -let subProtocol = 'https'; -let RproxyIP = 'false'; - -let addressesapi = []; -let addressescsv = []; -let DLS = 8; - -let FileName = 'epeius'; -let BotToken =''; -let ChatID =''; -let proxyhosts = [];//本地代理域名池 -let proxyhostsURL = 'https://raw.githubusercontent.com/cmliu/CFcdnVmess2sub/main/proxyhosts';//在线代理域名池URL -let go2Socks5s = [ - '*ttvnw.net', - '*tapecontent.net', - '*cloudatacdn.com', - '*.loadshare.org', -]; - -let fakeUserID ; -let fakeHostName ; -let proxyIPs ; -let socks5s; -let sha224Password ; -const expire = 4102329600;//2099-12-31 -const regex = /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[.*\]):?(\d+)?#?(.*)?$/; - -let parsedSocks5Address = {}; -let enableSocks = false; -let httpsPorts = ["2053","2083","2087","2096","8443"]; -/*Obfuscate-cmliu*/ -/* -if (!isValidSHA224(sha224Password)) { - throw new Error('sha224Password is not valid'); -} -*/ -export default { - async fetch(request, env, ctx) { - try { - const UA = request.headers.get('User-Agent') || 'null'; - const userAgent = UA.toLowerCase(); - password = env.PASSWORD || password; - sha224Password = env.SHA224 || env.SHA224PASS || sha256.sha224(password); - //console.log(sha224Password); - - const currentDate = new Date(); - currentDate.setHours(0, 0, 0, 0); // 设置时间为当天 - const timestamp = Math.ceil(currentDate.getTime() / 1000); - const fakeUserIDMD5 = await MD5MD5(`${password}${timestamp}`); - 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(fakeUserID); // 打印fakeID - - proxyIP = env.PROXYIP || proxyIP; - proxyIPs = await ADD(proxyIP); - proxyIP = proxyIPs[Math.floor(Math.random() * proxyIPs.length)]; - socks5Address = env.SOCKS5 || socks5Address; - socks5s = await ADD(socks5Address); - socks5Address = socks5s[Math.floor(Math.random() * socks5s.length)]; - socks5Address = socks5Address.split('//')[1] || socks5Address; - if (env.CFPORTS) httpsPorts = await ADD(env.CFPORTS); - sub = env.SUB || sub; - subconverter = env.SUBAPI || subconverter; - if( subconverter.includes("http://") ){ - subconverter = subconverter.split("//")[1]; - subProtocol = 'http'; - } else { - subconverter = subconverter.split("//")[1] || subconverter; - } - subconfig = env.SUBCONFIG || subconfig; - if (socks5Address) { - try { - parsedSocks5Address = socks5AddressParser(socks5Address); - RproxyIP = env.RPROXYIP || 'false'; - enableSocks = true; - } catch (err) { - /** @type {Error} */ - let e = err; - console.log(e.toString()); - RproxyIP = env.RPROXYIP || !proxyIP ? 'true' : 'false'; - enableSocks = false; - } - } else { - RproxyIP = env.RPROXYIP || !proxyIP ? 'true' : 'false'; - } - if (env.ADD) addresses = await ADD(env.ADD); - if (env.ADDAPI) addressesapi = await ADD(env.ADDAPI); - if (env.ADDCSV) addressescsv = await ADD(env.ADDCSV); - DLS = env.DLS || DLS; - BotToken = env.TGTOKEN || BotToken; - ChatID = env.TGID || ChatID; - if(env.GO2SOCKS5) go2Socks5s = await ADD(env.GO2SOCKS5); - const upgradeHeader = request.headers.get("Upgrade"); - const url = new URL(request.url); - if (url.searchParams.has('sub') && url.searchParams.get('sub') !== '') sub = url.searchParams.get('sub'); - FileName = env.SUBNAME || FileName; - if (!upgradeHeader || upgradeHeader !== "websocket") { - //const url = new URL(request.url); - switch (url.pathname) { - case '/': - 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), { - status: 200, - headers: { - 'content-type': 'application/json', - }, - }); - case `/${fakeUserID}`: - const fakeConfig = await getTrojanConfig(password, request.headers.get('Host'), sub, 'CF-Workers-SUB', RproxyIP, url); - return new Response(`${fakeConfig}`, { status: 200 }); - case `/${password}`: - await sendMessage(`#获取订阅 ${FileName}`, request.headers.get('CF-Connecting-IP'), `UA: ${UA}\n域名: ${url.hostname}\n入口: ${url.pathname + url.search}`); - const trojanConfig = await getTrojanConfig(password, request.headers.get('Host'), sub, UA, RproxyIP, url); - const now = Date.now(); - //const timestamp = Math.floor(now / 1000); - const today = new Date(now); - today.setHours(0, 0, 0, 0); - const UD = Math.floor(((now - today.getTime())/86400000) * 24 * 1099511627776 / 2); - let pagesSum = UD; - let workersSum = UD; - let total = 24 * 1099511627776 ; - if (env.CFEMAIL && env.CFKEY){ - const email = env.CFEMAIL; - const key = env.CFKEY; - const accountIndex = env.CFID || 0; - const accountId = await getAccountId(email, key); - if (accountId){ - const now = new Date() - now.setUTCHours(0, 0, 0, 0) - const startDate = now.toISOString() - const endDate = new Date().toISOString(); - const Sum = await getSum(accountId, accountIndex, email, key, startDate, endDate); - pagesSum = Sum[0]; - workersSum = Sum[1]; - total = 102400 ; - } - } - //console.log(`pagesSum: ${pagesSum}\nworkersSum: ${workersSum}\ntotal: ${total}`); - if (userAgent && (userAgent.includes('mozilla') || userAgent.includes('subconverter'))){ - return new Response(`${trojanConfig}`, { - status: 200, - headers: { - "Content-Type": "text/plain;charset=utf-8", - "Profile-Update-Interval": "6", - "Subscription-Userinfo": `upload=${pagesSum}; download=${workersSum}; total=${total}; expire=${expire}`, - } - }); - } else { - return new Response(`${trojanConfig}`, { - status: 200, - headers: { - "Content-Disposition": `attachment; filename=${FileName}; filename*=utf-8''${encodeURIComponent(FileName)}`, - "Content-Type": "text/plain;charset=utf-8", - "Profile-Update-Interval": "6", - "Subscription-Userinfo": `upload=${pagesSum}; download=${workersSum}; total=${total}; expire=${expire}`, - } - }); - } - default: - if (env.URL302) return Response.redirect(env.URL302, 302); - else if (env.URL) return await proxyURL(env.URL, url); - else return new Response('不用怀疑!你PASSWORD就是错的!!!', { status: 404 }); - } - } else { - proxyIP = url.searchParams.get('proxyip') || proxyIP; - if (new RegExp('/proxyip=', 'i').test(url.pathname)) proxyIP = url.pathname.toLowerCase().split('/proxyip=')[1]; - else if (new RegExp('/proxyip.', 'i').test(url.pathname)) proxyIP = `proxyip.${url.pathname.toLowerCase().split("/proxyip.")[1]}`; - - socks5Address = url.searchParams.get('socks5') || socks5Address; - if (new RegExp('/socks5=', 'i').test(url.pathname)) socks5Address = url.pathname.split('5=')[1]; - else if (new RegExp('/socks://', 'i').test(url.pathname) || new RegExp('/socks5://', 'i').test(url.pathname)) { - socks5Address = url.pathname.split('://')[1].split('#')[0]; - if (socks5Address.includes('@')){ - let userPassword = socks5Address.split('@')[0]; - const base64Regex = /^(?:[A-Z0-9+/]{4})*(?:[A-Z0-9+/]{2}==|[A-Z0-9+/]{3}=)?$/i; - if (base64Regex.test(userPassword) && !userPassword.includes(':')) userPassword = atob(userPassword); - socks5Address = `${userPassword}@${socks5Address.split('@')[1]}`; - } - } - if (socks5Address) { - try { - parsedSocks5Address = socks5AddressParser(socks5Address); - enableSocks = true; - } catch (err) { - /** @type {Error} */ - let e = err; - console.log(e.toString()); - enableSocks = false; - } - } else { - enableSocks = false; - } - return await trojanOverWSHandler(request); - } - } catch (err) { - let e = err; - return new Response(e.toString()); - } - } -}; - -async function trojanOverWSHandler(request) { - const webSocketPair = new WebSocketPair(); - const [client, webSocket] = Object.values(webSocketPair); - webSocket.accept(); - let address = ""; - let portWithRandomLog = ""; - const log = (info, event) => { - console.log(`[${address}:${portWithRandomLog}] ${info}`, event || ""); - }; - const earlyDataHeader = request.headers.get("sec-websocket-protocol") || ""; - const readableWebSocketStream = makeReadableWebSocketStream(webSocket, earlyDataHeader, log); - let remoteSocketWapper = { - value: null - }; - let udpStreamWrite = null; - readableWebSocketStream.pipeTo(new WritableStream({ - async write(chunk, controller) { - if (udpStreamWrite) { - return udpStreamWrite(chunk); - } - if (remoteSocketWapper.value) { - const writer = remoteSocketWapper.value.writable.getWriter(); - await writer.write(chunk); - writer.releaseLock(); - return; - } - const { - hasError, - message, - portRemote = 443, - addressRemote = "", - rawClientData, - addressType - } = await parseTrojanHeader(chunk); - address = addressRemote; - portWithRandomLog = `${portRemote}--${Math.random()} tcp`; - if (hasError) { - throw new Error(message); - return; - } - handleTCPOutBound(remoteSocketWapper, addressRemote, portRemote, rawClientData, webSocket, log, addressType); - }, - close() { - log(`readableWebSocketStream is closed`); - }, - abort(reason) { - log(`readableWebSocketStream is aborted`, JSON.stringify(reason)); - } - })).catch((err) => { - log("readableWebSocketStream pipeTo error", err); - }); - return new Response(null, { - status: 101, - // @ts-ignore - webSocket: client - }); -} - -async function parseTrojanHeader(buffer) { - if (buffer.byteLength < 56) { - return { - hasError: true, - message: "invalid data" - }; - } - let crLfIndex = 56; - if (new Uint8Array(buffer.slice(56, 57))[0] !== 0x0d || new Uint8Array(buffer.slice(57, 58))[0] !== 0x0a) { - return { - hasError: true, - message: "invalid header format (missing CR LF)" - }; - } - const password = new TextDecoder().decode(buffer.slice(0, crLfIndex)); - if (password !== sha224Password) { - return { - hasError: true, - message: "invalid password" - }; - } - - const socks5DataBuffer = buffer.slice(crLfIndex + 2); - if (socks5DataBuffer.byteLength < 6) { - return { - hasError: true, - message: "invalid SOCKS5 request data" - }; - } - - const view = new DataView(socks5DataBuffer); - const cmd = view.getUint8(0); - if (cmd !== 1) { - return { - hasError: true, - message: "unsupported command, only TCP (CONNECT) is allowed" - }; - } - - const atype = view.getUint8(1); - // 0x01: IPv4 address - // 0x03: Domain name - // 0x04: IPv6 address - let addressLength = 0; - let addressIndex = 2; - let address = ""; - switch (atype) { - case 1: - addressLength = 4; - address = new Uint8Array( - socks5DataBuffer.slice(addressIndex, addressIndex + addressLength) - ).join("."); - break; - case 3: - addressLength = new Uint8Array( - socks5DataBuffer.slice(addressIndex, addressIndex + 1) - )[0]; - addressIndex += 1; - address = new TextDecoder().decode( - socks5DataBuffer.slice(addressIndex, addressIndex + addressLength) - ); - break; - case 4: - addressLength = 16; - const dataView = new DataView(socks5DataBuffer.slice(addressIndex, addressIndex + addressLength)); - const ipv6 = []; - for (let i = 0; i < 8; i++) { - ipv6.push(dataView.getUint16(i * 2).toString(16)); - } - address = ipv6.join(":"); - break; - default: - return { - hasError: true, - message: `invalid addressType is ${atype}` - }; - } - - if (!address) { - return { - hasError: true, - message: `address is empty, addressType is ${atype}` - }; - } - - const portIndex = addressIndex + addressLength; - const portBuffer = socks5DataBuffer.slice(portIndex, portIndex + 2); - const portRemote = new DataView(portBuffer).getUint16(0); - return { - hasError: false, - addressRemote: address, - portRemote, - rawClientData: socks5DataBuffer.slice(portIndex + 4), - addressType: atype - }; -} - -async function handleTCPOutBound(remoteSocket, addressRemote, portRemote, rawClientData, webSocket, log, addressType) { - async function useSocks5Pattern(address) { - if ( go2Socks5s.includes(atob('YWxsIGlu')) || go2Socks5s.includes(atob('Kg==')) ) return true; - return go2Socks5s.some(pattern => { - let regexPattern = pattern.replace(/\*/g, '.*'); - let regex = new RegExp(`^${regexPattern}$`, 'i'); - return regex.test(address); - }); - } - async function connectAndWrite(address, port, socks = false) { - log(`connected to ${address}:${port}`); - //if (/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(address)) address = `${atob('d3d3Lg==')}${address}${atob('LmlwLjA5MDIyNy54eXo=')}`; - const tcpSocket = socks ? await socks5Connect(addressType, address, port, log) - : connect({ - hostname: address, - port - }); - remoteSocket.value = tcpSocket; - //log(`connected to ${address}:${port}`); - const writer = tcpSocket.writable.getWriter(); - await writer.write(rawClientData); - writer.releaseLock(); - return tcpSocket; - } - async function retry() { - if (enableSocks) { - tcpSocket = await connectAndWrite(addressRemote, portRemote, true); - } else { - if (!proxyIP || proxyIP == '') { - proxyIP = atob('cHJveHlpcC50cDEuY21saXVzc3NzLmNvbQ=='); - } else if (proxyIP.includes(']:')) { - portRemote = proxyIP.split(']:')[1] || portRemote; - proxyIP = proxyIP.split(']:')[0] || proxyIP; - } else if (proxyIP.split(':').length === 2) { - 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); - } - tcpSocket.closed.catch((error) => { - console.log("retry tcpSocket closed error", error); - }).finally(() => { - safeCloseWebSocket(webSocket); - }); - remoteSocketToWS(tcpSocket, webSocket, null, log); - } - let useSocks = false; - if( go2Socks5s.length > 0 && enableSocks ) useSocks = await useSocks5Pattern(addressRemote); - let tcpSocket = await connectAndWrite(addressRemote, portRemote, useSocks); - remoteSocketToWS(tcpSocket, webSocket, retry, log); -} - -function makeReadableWebSocketStream(webSocketServer, earlyDataHeader, log) { - let readableStreamCancel = false; - const stream = new ReadableStream({ - start(controller) { - webSocketServer.addEventListener("message", (event) => { - if (readableStreamCancel) { - return; - } - const message = event.data; - controller.enqueue(message); - }); - webSocketServer.addEventListener("close", () => { - safeCloseWebSocket(webSocketServer); - if (readableStreamCancel) { - return; - } - controller.close(); - }); - webSocketServer.addEventListener("error", (err) => { - log("webSocketServer error"); - controller.error(err); - }); - const { earlyData, error } = base64ToArrayBuffer(earlyDataHeader); - if (error) { - controller.error(error); - } else if (earlyData) { - controller.enqueue(earlyData); - } - }, - pull(controller) {}, - cancel(reason) { - if (readableStreamCancel) { - return; - } - log(`readableStream was canceled, due to ${reason}`); - readableStreamCancel = true; - safeCloseWebSocket(webSocketServer); - } - }); - return stream; -} - -async function remoteSocketToWS(remoteSocket, webSocket, retry, log) { - let hasIncomingData = false; - await remoteSocket.readable.pipeTo( - new WritableStream({ - start() {}, - /** - * - * @param {Uint8Array} chunk - * @param {*} controller - */ - async write(chunk, controller) { - hasIncomingData = true; - if (webSocket.readyState !== WS_READY_STATE_OPEN) { - controller.error( - "webSocket connection is not open" - ); - } - webSocket.send(chunk); - }, - close() { - log(`remoteSocket.readable is closed, hasIncomingData: ${hasIncomingData}`); - }, - abort(reason) { - console.error("remoteSocket.readable abort", reason); - } - }) - ).catch((error) => { - console.error( - `remoteSocketToWS error:`, - error.stack || error - ); - safeCloseWebSocket(webSocket); - }); - if (hasIncomingData === false && retry) { - log(`retry`); - retry(); - } -} -/* -function isValidSHA224(hash) { - const sha224Regex = /^[0-9a-f]{56}$/i; - return sha224Regex.test(hash); -} -*/ -function base64ToArrayBuffer(base64Str) { - if (!base64Str) { - return { error: null }; - } - try { - base64Str = base64Str.replace(/-/g, "+").replace(/_/g, "/"); - const decode = atob(base64Str); - const arryBuffer = Uint8Array.from(decode, (c) => c.charCodeAt(0)); - return { earlyData: arryBuffer.buffer, error: null }; - } catch (error) { - return { error }; - } -} - -let WS_READY_STATE_OPEN = 1; -let WS_READY_STATE_CLOSING = 2; - -function safeCloseWebSocket(socket) { - try { - if (socket.readyState === WS_READY_STATE_OPEN || socket.readyState === WS_READY_STATE_CLOSING) { - socket.close(); - } - } catch (error) { - console.error("safeCloseWebSocket error", error); - } -} - -/* -export { - worker_default as - default -}; -//# sourceMappingURL=worker.js.map -*/ - -function revertFakeInfo(content, userID, hostName, isBase64) { - if (isBase64) content = atob(content);//Base64解码 - content = content.replace(new RegExp(fakeUserID, 'g'), userID).replace(new RegExp(fakeHostName, 'g'), hostName); - //console.log(content); - if (isBase64) content = btoa(content);//Base64编码 - - return content; -} - -async function MD5MD5(text) { - const encoder = new TextEncoder(); - - const firstPass = await crypto.subtle.digest('MD5', encoder.encode(text)); - const firstPassArray = Array.from(new Uint8Array(firstPass)); - const firstHex = firstPassArray.map(b => b.toString(16).padStart(2, '0')).join(''); - - const secondPass = await crypto.subtle.digest('MD5', encoder.encode(firstHex.slice(7, 27))); - const secondPassArray = Array.from(new Uint8Array(secondPass)); - const secondHex = secondPassArray.map(b => b.toString(16).padStart(2, '0')).join(''); - - return secondHex.toLowerCase(); -} - -async function ADD(envadd) { - var addtext = envadd.replace(/[ |"'\r\n]+/g, ',').replace(/,+/g, ','); // 双引号、单引号和换行符替换为逗号 - //console.log(addtext); - if (addtext.charAt(0) == ',') addtext = addtext.slice(1); - if (addtext.charAt(addtext.length -1) == ',') addtext = addtext.slice(0, addtext.length - 1); - const add = addtext.split(','); - //console.log(add); - return add ; -} - -async function proxyURL(proxyURL, url) { - const URLs = await ADD(proxyURL); - const fullURL = URLs[Math.floor(Math.random() * URLs.length)]; - // 解析目标 URL - let parsedURL = new URL(fullURL); - console.log(parsedURL); - // 提取并可能修改 URL 组件 - let URLProtocol = parsedURL.protocol.slice(0, -1) || 'https'; - let URLHostname = parsedURL.hostname; - let URLPathname = parsedURL.pathname; - let URLSearch = parsedURL.search; - // 处理 pathname - if (URLPathname.charAt(URLPathname.length - 1) == '/') { - URLPathname = URLPathname.slice(0, -1); - } - URLPathname += url.pathname; - // 构建新的 URL - let newURL = `${URLProtocol}://${URLHostname}${URLPathname}${URLSearch}`; - // 反向代理请求 - let response = await fetch(newURL); - // 创建新的响应 - let newResponse = new Response(response.body, { - status: response.status, - statusText: response.statusText, - headers: response.headers - }); - // 添加自定义头部,包含 URL 信息 - //newResponse.headers.set('X-Proxied-By', 'Cloudflare Worker'); - //newResponse.headers.set('X-Original-URL', fullURL); - newResponse.headers.set('X-New-URL', newURL); - return newResponse; -} - -function checkSUB(host) { - if ((!sub || sub == '') && (addresses.length + addressesapi.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', - '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', - 'fbi.gov:2087', - 'icook.hk', - //'104.17.0.0#IPv4', - '[2606:4700::]#IPv6' - ]; - } -} - -function 配置信息(密码, 域名地址) { - const 啥啥啥_写的这是啥啊 = 'dHJvamFu'; - const 协议类型 = atob(啥啥啥_写的这是啥啊); - - const 别名 = FileName; - let 地址 = 域名地址; - let 端口 = 443; - - const 传输层协议 = 'ws'; - const 伪装域名 = 域名地址; - const 路径 = '/?ed=2560'; - - let 传输层安全 = ['tls',true]; - const SNI = 域名地址; - const 指纹 = 'randomized'; - - const v2ray = `${协议类型}://${encodeURIComponent(密码)}@${地址}:${端口}?security=${传输层安全[0]}&sni=${SNI}&alpn=h3&fp=${指纹}&allowInsecure=1&type=${传输层协议}&host=${伪装域名}&path=${encodeURIComponent(路径)}#${encodeURIComponent(别名)}` - const clash = `- {name: ${别名}, server: ${地址}, port: ${端口}, udp: false, client-fingerprint: ${指纹}, type: ${协议类型}, password: ${密码}, sni: ${SNI}, alpn: [h3], skip-cert-verify: true, network: ${传输层协议}, ws-opts: {path: "${路径}", headers: {Host: ${伪装域名}}}}`; - - return [v2ray,clash]; -} - -let subParams = ['sub','base64','b64','clash','singbox','sb','surge']; -async function getTrojanConfig(password, hostName, sub, UA, RproxyIP, _url) { - checkSUB(hostName); - const userAgent = UA.toLowerCase(); - const Config = 配置信息(password , hostName); - const v2ray = Config[0]; - const clash = Config[1]; - let proxyhost = ""; - if(hostName.includes(".workers.dev") || hostName.includes(".pages.dev")){ - if ( proxyhostsURL && (!proxyhosts || proxyhosts.length == 0)) { - try { - const response = await fetch(proxyhostsURL); - - if (!response.ok) { - console.error('获取地址时出错:', response.status, response.statusText); - return; // 如果有错误,直接返回 - } - - const text = await response.text(); - const lines = text.split('\n'); - // 过滤掉空行或只包含空白字符的行 - const nonEmptyLines = lines.filter(line => line.trim() !== ''); - - proxyhosts = proxyhosts.concat(nonEmptyLines); - } catch (error) { - //console.error('获取地址时出错:', error); - } - } - if (proxyhosts.length != 0) proxyhost = proxyhosts[Math.floor(Math.random() * proxyhosts.length)] + "/"; - } - - if ( userAgent.includes('mozilla') && !subParams.some(_searchParams => _url.searchParams.has(_searchParams))) { - let surge = `Surge订阅地址:\nhttps://${proxyhost}${hostName}/${password}?surge`; - if (hostName.includes(".workers.dev") || hostName.includes(".pages.dev")) surge = "Surge订阅必须绑定自定义域"; - const newSocks5s = socks5s.map(socks5Address => { - if (socks5Address.includes('@')) return socks5Address.split('@')[1]; - else if (socks5Address.includes('//')) return socks5Address.split('//')[1]; - else return socks5Address; - }); - - let socks5List = ''; - if( go2Socks5s.length > 0 && enableSocks ) { - socks5List = `${decodeURIComponent('SOCKS5%EF%BC%88%E7%99%BD%E5%90%8D%E5%8D%95%EF%BC%89%3A%20')}`; - if ( go2Socks5s.includes(atob('YWxsIGlu')) || go2Socks5s.includes(atob('Kg==')) ) socks5List += `${decodeURIComponent('%E6%89%80%E6%9C%89%E6%B5%81%E9%87%8F')}\n`; - else socks5List += `\n ${go2Socks5s.join('\n ')}\n`; - } - - let 订阅器 = ''; - if (!sub || sub == '') { - if (enableSocks) 订阅器 += `CFCDN(访问方式): Socks5\n ${newSocks5s.join('\n ')}\n${socks5List}`; - else if (proxyIP && proxyIP != '') 订阅器 += `CFCDN(访问方式): ProxyIP\n ${proxyIPs.join('\n ')}\n`; - else 订阅器 += `CFCDN(访问方式): 无法访问, 需要您设置 proxyIP/PROXYIP !!!\n`; - 订阅器 += `\n您的订阅内容由 内置 addresses/ADD* 参数变量提供\n`; - if (addresses.length > 0) 订阅器 += `ADD(TLS优选域名&IP): \n ${addresses.join('\n ')}\n`; - if (addressesapi.length > 0) 订阅器 += `ADDAPI(TLS优选域名&IP 的 API): \n ${addressesapi.join('\n ')}\n`; - if (addressescsv.length > 0) 订阅器 += `ADDCSV(IPTest测速csv文件 限速 ${DLS} ): \n ${addressescsv.join('\n ')}\n`; - } else { - if (enableSocks) 订阅器 += `CFCDN(访问方式): Socks5\n ${newSocks5s.join('\n ')}\n${socks5List}`; - else if (proxyIP && proxyIP != '') 订阅器 += `CFCDN(访问方式): ProxyIP\n ${proxyIPs.join('\n ')}\n`; - else if (RproxyIP == 'true') 订阅器 += `CFCDN(访问方式): 自动获取ProxyIP\n`; - else 订阅器 += `CFCDN(访问方式): 无法访问, 需要您设置 proxyIP/PROXYIP !!!\n` - 订阅器 += `\nSUB(优选订阅生成器): ${sub}`; - } - - return ` -################################################################ -Subscribe / sub 订阅地址, 支持 Base64、clash-meta、sing-box 订阅格式 ---------------------------------------------------------------- -快速自适应订阅地址: -https://${proxyhost}${hostName}/${password} -https://${proxyhost}${hostName}/${password}?sub - -Base64订阅地址: -https://${proxyhost}${hostName}/${password}?b64 -https://${proxyhost}${hostName}/${password}?base64 - -clash订阅地址: -https://${proxyhost}${hostName}/${password}?clash - -singbox订阅地址: -https://${proxyhost}${hostName}/${password}?sb -https://${proxyhost}${hostName}/${password}?singbox - -${surge} ---------------------------------------------------------------- -################################################################ -${FileName} 配置信息 ---------------------------------------------------------------- -HOST: ${hostName} -PASSWORD: ${password} -SHA224: ${sha224Password} -FAKEPASS: ${fakeUserID} -UA: ${UA} - -${订阅器} -SUBAPI(订阅转换后端): ${subProtocol}://${subconverter} -SUBCONFIG(订阅转换配置文件): ${subconfig} ---------------------------------------------------------------- -################################################################ -v2ray ---------------------------------------------------------------- -${v2ray} ---------------------------------------------------------------- -################################################################ -clash-meta ---------------------------------------------------------------- -${clash} ---------------------------------------------------------------- -################################################################ -telegram 交流群 技术大佬~在线发牌! -https://t.me/CMLiussss ---------------------------------------------------------------- -github 项目地址 Star!Star!Star!!! -https://github.com/cmliu/epeius ---------------------------------------------------------------- -################################################################ -`; - } else { - if (typeof fetch != 'function') { - return 'Error: fetch is not available in this environment.'; - } - // 如果是使用默认域名,则改成一个workers的域名,订阅器会加上代理 - if (hostName.includes(".workers.dev") || hostName.includes(".pages.dev")){ - fakeHostName = `${fakeHostName}.workers.dev`; - } else { - fakeHostName = `${fakeHostName}.xyz` - } - - let url = `https://${sub}/sub?host=${fakeHostName}&pw=${fakeUserID}&password=${fakeUserID}&epeius=cmliu&proxyip=${RproxyIP}`; - let isBase64 = true; - let newAddressesapi = []; - let newAddressescsv = []; - - if (!sub || sub == "") { - if(hostName.includes('workers.dev') || hostName.includes('pages.dev')) { - if (proxyhostsURL && (!proxyhosts || proxyhosts.length == 0)) { - try { - const response = await fetch(proxyhostsURL); - - if (!response.ok) { - console.error('获取地址时出错:', response.status, response.statusText); - return; // 如果有错误,直接返回 - } - - const text = await response.text(); - const lines = text.split('\n'); - // 过滤掉空行或只包含空白字符的行 - const nonEmptyLines = lines.filter(line => line.trim() !== ''); - - proxyhosts = proxyhosts.concat(nonEmptyLines); - } catch (error) { - console.error('获取地址时出错:', error); - } - } - // 使用Set对象去重 - proxyhosts = [...new Set(proxyhosts)]; - } - - newAddressesapi = await getAddressesapi(addressesapi); - newAddressescsv = await getAddressescsv('TRUE'); - url = `https://${hostName}/${fakeUserID}`; - } - - if (!userAgent.includes(('CF-Workers-SUB').toLowerCase())){ - if ((userAgent.includes('clash') && !userAgent.includes('nekobox')) || ( _url.searchParams.has('clash'))) { - url = `${subProtocol}://${subconverter}/sub?target=clash&url=${encodeURIComponent(url)}&insert=false&config=${encodeURIComponent(subconfig)}&emoji=true&list=false&tfo=false&scv=true&fdn=false&sort=false&new_name=true`; - isBase64 = false; - } else if (userAgent.includes('sing-box') || userAgent.includes('singbox') || _url.searchParams.has('singbox') || _url.searchParams.has('sb')) { - url = `${subProtocol}://${subconverter}/sub?target=singbox&url=${encodeURIComponent(url)}&insert=false&config=${encodeURIComponent(subconfig)}&emoji=true&list=false&tfo=false&scv=true&fdn=false&sort=false&new_name=true`; - isBase64 = false; - } else if (userAgent.includes('surge') || _url.searchParams.has('surge')) { - url = `${subProtocol}://${subconverter}/sub?target=surge&ver=4&url=${encodeURIComponent(url)}&insert=false&config=${encodeURIComponent(subconfig)}&emoji=true&list=false&xudp=false&udp=false&tfo=false&expand=true&scv=true&fdn=false`; - isBase64 = false; - } - } - - try { - let content; - if ((!sub || sub == "") && isBase64 == true) { - content = await subAddresses(fakeHostName,fakeUserID,userAgent,newAddressesapi,newAddressescsv); - } else { - const response = await fetch(url ,{ - headers: { - 'User-Agent': `CF-Workers-epeius/cmliu` - }}); - content = await response.text(); - } - - if (_url.pathname == `/${fakeUserID}`) return content; - - content = revertFakeInfo(content, password, hostName, isBase64); - if (userAgent.includes('surge') || _url.searchParams.has('surge')) content = surge(content, `https://${hostName}/${password}?surge`); - return content; - } catch (error) { - console.error('Error fetching content:', error); - return `Error fetching content: ${error.message}`; - } - } -} - -async function sendMessage(type, ip, add_data = "") { - if ( BotToken !== '' && ChatID !== ''){ - let msg = ""; - const response = await fetch(`http://ip-api.com/json/${ip}?lang=zh-CN`); - if (response.status == 200) { - const ipInfo = await response.json(); - msg = `${type}\nIP: ${ip}\n国家: ${ipInfo.country}\n城市: ${ipInfo.city}\n组织: ${ipInfo.org}\nASN: ${ipInfo.as}\n${add_data}`; - } else { - msg = `${type}\nIP: ${ip}\n${add_data}`; - } - - let url = "https://api.telegram.org/bot"+ BotToken +"/sendMessage?chat_id=" + ChatID + "&parse_mode=HTML&text=" + encodeURIComponent(msg); - return fetch(url, { - method: 'get', - headers: { - 'Accept': 'text/html,application/xhtml+xml,application/xml;', - 'Accept-Encoding': 'gzip, deflate, br', - 'User-Agent': 'Mozilla/5.0 Chrome/90.0.4430.72' - } - }); - } -} -let proxyIPPool = []; -function subAddresses(host,pw,userAgent,newAddressesapi,newAddressescsv) { - addresses = addresses.concat(newAddressesapi); - addresses = addresses.concat(newAddressescsv); - // 使用Set对象去重 - const uniqueAddresses = [...new Set(addresses)]; - - const responseBody = uniqueAddresses.map(address => { - let port = "-1"; - let addressid = address; - - const match = addressid.match(regex); - if (!match) { - if (address.includes(':') && address.includes('#')) { - const parts = address.split(':'); - address = parts[0]; - const subParts = parts[1].split('#'); - port = subParts[0]; - addressid = subParts[1]; - } else if (address.includes(':')) { - const parts = address.split(':'); - address = parts[0]; - port = parts[1]; - } else if (address.includes('#')) { - const parts = address.split('#'); - address = parts[0]; - addressid = parts[1]; - } - - if (addressid.includes(':')) { - addressid = addressid.split(':')[0]; - } - } else { - address = match[1]; - port = match[2] || port; - addressid = match[3] || address; - } - - const httpsPorts = ["2053","2083","2087","2096","8443"]; - if (!isValidIPv4(address) && port == "-1") { - for (let httpsPort of httpsPorts) { - if (address.includes(httpsPort)) { - port = httpsPort; - break; - } - } - } - if (port == "-1") port = "443"; - - let 伪装域名 = host ; - let 最终路径 = '/?ed=2560' ; - let 节点备注 = ''; - - if(proxyhosts.length > 0 && (伪装域名.includes('.workers.dev') || 伪装域名.includes('pages.dev'))) { - 最终路径 = `/${伪装域名}${最终路径}`; - 伪装域名 = proxyhosts[Math.floor(Math.random() * proxyhosts.length)]; - 节点备注 = ` 已启用临时域名中转服务,请尽快绑定自定义域!`; - } - const matchingProxyIP = proxyIPPool.find(proxyIP => proxyIP.includes(address)); - if (matchingProxyIP) 最终路径 += `&proxyip=${matchingProxyIP}`; - let 密码 = pw; - if (!userAgent.includes('subconverter')) 密码 = encodeURIComponent(pw); - - const 啥啥啥_写的这是啥啊 = 'dHJvamFu'; - const 协议类型 = atob(啥啥啥_写的这是啥啊); - const trojanLink = `${协议类型}://${密码}@${address}:${port}?security=tls&sni=${伪装域名}&fp=randomized&type=ws&host=${伪装域名}&path=${encodeURIComponent(最终路径)}#${encodeURIComponent(addressid + 节点备注)}`; - - return trojanLink; - }).join('\n'); - - const base64Response = btoa(responseBody); // 重新进行 Base64 编码 - - return base64Response; -} - -async function getAddressesapi(api) { - if (!api || api.length === 0) return []; - - let newapi = ""; - - // 创建一个AbortController对象,用于控制fetch请求的取消 - const controller = new AbortController(); - - const timeout = setTimeout(() => { - controller.abort(); // 取消所有请求 - }, 2000); // 2秒后触发 - - try { - // 使用Promise.allSettled等待所有API请求完成,无论成功或失败 - // 对api数组进行遍历,对每个API地址发起fetch请求 - const responses = await Promise.allSettled(api.map(apiUrl => fetch(apiUrl, { - method: 'get', - headers: { - 'Accept': 'text/html,application/xhtml+xml,application/xml;', - 'User-Agent': 'CF-Workers-epeius/cmliu' - }, - signal: controller.signal // 将AbortController的信号量添加到fetch请求中,以便于需要时可以取消请求 - }).then(response => response.ok ? response.text() : Promise.reject()))); - - // 遍历所有响应 - for (const [index, response] of responses.entries()) { - // 检查响应状态是否为'fulfilled',即请求成功完成 - if (response.status === 'fulfilled') { - // 获取响应的内容 - const content = await response.value; - - // 验证当前apiUrl是否带有'proxyip=true' - if (api[index].includes('proxyip=true')) { - // 如果URL带有'proxyip=true',则将内容添加到proxyIPPool - proxyIPPool = proxyIPPool.concat((await ADD(content)).map(item => { - const baseItem = item.split('#')[0] || item; - if (baseItem.includes(':')) { - const port = baseItem.split(':')[1]; - if (!httpsPorts.includes(port)) { - return baseItem; - } - } else { - return `${baseItem}:443`; - } - return null; // 不符合条件时返回 null - }).filter(Boolean)); // 过滤掉 null 值 - } - // 将内容添加到newapi中 - newapi += content + '\n'; - } - } - } catch (error) { - console.error(error); - } finally { - // 无论成功或失败,最后都清除设置的超时定时器 - clearTimeout(timeout); - } - - const newAddressesapi = await ADD(newapi); - - // 返回处理后的结果 - return newAddressesapi; -} - -async function getAddressescsv(tls) { - if (!addressescsv || addressescsv.length === 0) { - return []; - } - - let newAddressescsv = []; - - for (const csvUrl of addressescsv) { - try { - const response = await fetch(csvUrl); - - if (!response.ok) { - console.error('获取CSV地址时出错:', response.status, response.statusText); - continue; - } - - const text = await response.text();// 使用正确的字符编码解析文本内容 - let lines; - if (text.includes('\r\n')){ - lines = text.split('\r\n'); - } else { - lines = text.split('\n'); - } - - // 检查CSV头部是否包含必需字段 - const header = lines[0].split(','); - const tlsIndex = header.indexOf('TLS'); - - const ipAddressIndex = 0;// IP地址在 CSV 头部的位置 - const portIndex = 1;// 端口在 CSV 头部的位置 - const dataCenterIndex = tlsIndex + 1; // 数据中心是 TLS 的后一个字段 - - if (tlsIndex === -1) { - console.error('CSV文件缺少必需的字段'); - continue; - } - - // 从第二行开始遍历CSV行 - for (let i = 1; i < lines.length; i++) { - const columns = lines[i].split(','); - const speedIndex = columns.length - 1; // 最后一个字段 - // 检查TLS是否为"TRUE"且速度大于DLS - if (columns[tlsIndex].toUpperCase() === tls && parseFloat(columns[speedIndex]) > DLS) { - const ipAddress = columns[ipAddressIndex]; - const port = columns[portIndex]; - const dataCenter = columns[dataCenterIndex]; - - const formattedAddress = `${ipAddress}:${port}#${dataCenter}`; - newAddressescsv.push(formattedAddress); - if (csvUrl.includes('proxyip=true') && columns[tlsIndex].toUpperCase() == 'true' && !httpsPorts.includes(port)) { - // 如果URL带有'proxyip=true',则将内容添加到proxyIPPool - proxyIPPool.push(`${ipAddress}:${port}`); - } - } - } - } catch (error) { - console.error('获取CSV地址时出错:', error); - continue; - } - } - - return newAddressescsv; -} - -function surge(content, url) { - let 每行内容; - if (content.includes('\r\n')){ - 每行内容 = content.split('\r\n'); - } else { - 每行内容 = content.split('\n'); - } - - let 输出内容 = ""; - for (let x of 每行内容) { - if (x.includes('= trojan,')) { - const host = x.split("sni=")[1].split(",")[0]; - const 备改内容 = `skip-cert-verify=true, tfo=false, udp-relay=false`; - const 正确内容 = `skip-cert-verify=true, ws=true, ws-path=/?ed=2560, ws-headers=Host:"${host}", tfo=false, udp-relay=false`; - 输出内容 += x.replace(new RegExp(备改内容, 'g'), 正确内容).replace("[", "").replace("]", "") + '\n'; - } else { - 输出内容 += x + '\n'; - } - } - - 输出内容 = `#!MANAGED-CONFIG ${url} interval=86400 strict=false` + 输出内容.substring(输出内容.indexOf('\n')); - return 输出内容; -} - -/** - * [js-sha256]{@link https://github.com/emn178/js-sha256} - * - * @version 0.11.0 - * @author Chen, Yi-Cyuan [emn178@gmail.com] - * @copyright Chen, Yi-Cyuan 2014-2024 - * @license MIT - */ -/*jslint bitwise: true */ -(function () { - 'use strict'; - - var ERROR = 'input is invalid type'; - var WINDOW = typeof window === 'object'; - var root = WINDOW ? window : {}; - if (root.JS_SHA256_NO_WINDOW) { - WINDOW = false; - } - var WEB_WORKER = !WINDOW && typeof self === 'object'; - var NODE_JS = !root.JS_SHA256_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node; - if (NODE_JS) { - root = global; - } else if (WEB_WORKER) { - root = self; - } - var COMMON_JS = !root.JS_SHA256_NO_COMMON_JS && typeof module === 'object' && module.exports; - var AMD = typeof define === 'function' && define.amd; - var ARRAY_BUFFER = !root.JS_SHA256_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined'; - var HEX_CHARS = '0123456789abcdef'.split(''); - var EXTRA = [-2147483648, 8388608, 32768, 128]; - var SHIFT = [24, 16, 8, 0]; - var K = [ - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 - ]; - var OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer']; - - var blocks = []; - - if (root.JS_SHA256_NO_NODE_JS || !Array.isArray) { - Array.isArray = function (obj) { - return Object.prototype.toString.call(obj) === '[object Array]'; - }; - } - - if (ARRAY_BUFFER && (root.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) { - ArrayBuffer.isView = function (obj) { - return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer; - }; - } - - var createOutputMethod = function (outputType, is224) { - return function (message) { - return new Sha256(is224, true).update(message)[outputType](); - }; - }; - - var createMethod = function (is224) { - var method = createOutputMethod('hex', is224); - if (NODE_JS) { - method = nodeWrap(method, is224); - } - method.create = function () { - return new Sha256(is224); - }; - method.update = function (message) { - return method.create().update(message); - }; - for (var i = 0; i < OUTPUT_TYPES.length; ++i) { - var type = OUTPUT_TYPES[i]; - method[type] = createOutputMethod(type, is224); - } - return method; - }; - - var nodeWrap = function (method, is224) { - var crypto = require('crypto') - var Buffer = require('buffer').Buffer; - var algorithm = is224 ? 'sha224' : 'sha256'; - var bufferFrom; - if (Buffer.from && !root.JS_SHA256_NO_BUFFER_FROM) { - bufferFrom = Buffer.from; - } else { - bufferFrom = function (message) { - return new Buffer(message); - }; - } - var nodeMethod = function (message) { - if (typeof message === 'string') { - return crypto.createHash(algorithm).update(message, 'utf8').digest('hex'); - } else { - if (message === null || message === undefined) { - throw new Error(ERROR); - } else if (message.constructor === ArrayBuffer) { - message = new Uint8Array(message); - } - } - if (Array.isArray(message) || ArrayBuffer.isView(message) || - message.constructor === Buffer) { - return crypto.createHash(algorithm).update(bufferFrom(message)).digest('hex'); - } else { - return method(message); - } - }; - return nodeMethod; - }; - - var createHmacOutputMethod = function (outputType, is224) { - return function (key, message) { - return new HmacSha256(key, is224, true).update(message)[outputType](); - }; - }; - - var createHmacMethod = function (is224) { - var method = createHmacOutputMethod('hex', is224); - method.create = function (key) { - return new HmacSha256(key, is224); - }; - method.update = function (key, message) { - return method.create(key).update(message); - }; - for (var i = 0; i < OUTPUT_TYPES.length; ++i) { - var type = OUTPUT_TYPES[i]; - method[type] = createHmacOutputMethod(type, is224); - } - return method; - }; - - function Sha256(is224, sharedMemory) { - if (sharedMemory) { - blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = - blocks[4] = blocks[5] = blocks[6] = blocks[7] = - blocks[8] = blocks[9] = blocks[10] = blocks[11] = - blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; - this.blocks = blocks; - } else { - this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - } - - if (is224) { - this.h0 = 0xc1059ed8; - this.h1 = 0x367cd507; - this.h2 = 0x3070dd17; - this.h3 = 0xf70e5939; - this.h4 = 0xffc00b31; - this.h5 = 0x68581511; - this.h6 = 0x64f98fa7; - this.h7 = 0xbefa4fa4; - } else { // 256 - this.h0 = 0x6a09e667; - this.h1 = 0xbb67ae85; - this.h2 = 0x3c6ef372; - this.h3 = 0xa54ff53a; - this.h4 = 0x510e527f; - this.h5 = 0x9b05688c; - this.h6 = 0x1f83d9ab; - this.h7 = 0x5be0cd19; - } - - this.block = this.start = this.bytes = this.hBytes = 0; - this.finalized = this.hashed = false; - this.first = true; - this.is224 = is224; - } - - Sha256.prototype.update = function (message) { - if (this.finalized) { - return; - } - var notString, type = typeof message; - if (type !== 'string') { - if (type === 'object') { - if (message === null) { - throw new Error(ERROR); - } else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) { - message = new Uint8Array(message); - } else if (!Array.isArray(message)) { - if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) { - throw new Error(ERROR); - } - } - } else { - throw new Error(ERROR); - } - notString = true; - } - var code, index = 0, i, length = message.length, blocks = this.blocks; - while (index < length) { - if (this.hashed) { - this.hashed = false; - blocks[0] = this.block; - this.block = blocks[16] = blocks[1] = blocks[2] = blocks[3] = - blocks[4] = blocks[5] = blocks[6] = blocks[7] = - blocks[8] = blocks[9] = blocks[10] = blocks[11] = - blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; - } - - if (notString) { - for (i = this.start; index < length && i < 64; ++index) { - blocks[i >>> 2] |= message[index] << SHIFT[i++ & 3]; - } - } else { - for (i = this.start; index < length && i < 64; ++index) { - code = message.charCodeAt(index); - if (code < 0x80) { - blocks[i >>> 2] |= code << SHIFT[i++ & 3]; - } else if (code < 0x800) { - blocks[i >>> 2] |= (0xc0 | (code >>> 6)) << SHIFT[i++ & 3]; - blocks[i >>> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } else if (code < 0xd800 || code >= 0xe000) { - blocks[i >>> 2] |= (0xe0 | (code >>> 12)) << SHIFT[i++ & 3]; - blocks[i >>> 2] |= (0x80 | ((code >>> 6) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >>> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } else { - code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff)); - blocks[i >>> 2] |= (0xf0 | (code >>> 18)) << SHIFT[i++ & 3]; - blocks[i >>> 2] |= (0x80 | ((code >>> 12) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >>> 2] |= (0x80 | ((code >>> 6) & 0x3f)) << SHIFT[i++ & 3]; - blocks[i >>> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; - } - } - } - - this.lastByteIndex = i; - this.bytes += i - this.start; - if (i >= 64) { - this.block = blocks[16]; - this.start = i - 64; - this.hash(); - this.hashed = true; - } else { - this.start = i; - } - } - if (this.bytes > 4294967295) { - this.hBytes += this.bytes / 4294967296 << 0; - this.bytes = this.bytes % 4294967296; - } - return this; - }; - - Sha256.prototype.finalize = function () { - if (this.finalized) { - return; - } - this.finalized = true; - var blocks = this.blocks, i = this.lastByteIndex; - blocks[16] = this.block; - blocks[i >>> 2] |= EXTRA[i & 3]; - this.block = blocks[16]; - if (i >= 56) { - if (!this.hashed) { - this.hash(); - } - blocks[0] = this.block; - blocks[16] = blocks[1] = blocks[2] = blocks[3] = - blocks[4] = blocks[5] = blocks[6] = blocks[7] = - blocks[8] = blocks[9] = blocks[10] = blocks[11] = - blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; - } - blocks[14] = this.hBytes << 3 | this.bytes >>> 29; - blocks[15] = this.bytes << 3; - this.hash(); - }; - - Sha256.prototype.hash = function () { - var a = this.h0, b = this.h1, c = this.h2, d = this.h3, e = this.h4, f = this.h5, g = this.h6, - h = this.h7, blocks = this.blocks, j, s0, s1, maj, t1, t2, ch, ab, da, cd, bc; - - for (j = 16; j < 64; ++j) { - // rightrotate - t1 = blocks[j - 15]; - s0 = ((t1 >>> 7) | (t1 << 25)) ^ ((t1 >>> 18) | (t1 << 14)) ^ (t1 >>> 3); - t1 = blocks[j - 2]; - s1 = ((t1 >>> 17) | (t1 << 15)) ^ ((t1 >>> 19) | (t1 << 13)) ^ (t1 >>> 10); - blocks[j] = blocks[j - 16] + s0 + blocks[j - 7] + s1 << 0; - } - - bc = b & c; - for (j = 0; j < 64; j += 4) { - if (this.first) { - if (this.is224) { - ab = 300032; - t1 = blocks[0] - 1413257819; - h = t1 - 150054599 << 0; - d = t1 + 24177077 << 0; - } else { - ab = 704751109; - t1 = blocks[0] - 210244248; - h = t1 - 1521486534 << 0; - d = t1 + 143694565 << 0; - } - this.first = false; - } else { - s0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10)); - s1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7)); - ab = a & b; - maj = ab ^ (a & c) ^ bc; - ch = (e & f) ^ (~e & g); - t1 = h + s1 + ch + K[j] + blocks[j]; - t2 = s0 + maj; - h = d + t1 << 0; - d = t1 + t2 << 0; - } - s0 = ((d >>> 2) | (d << 30)) ^ ((d >>> 13) | (d << 19)) ^ ((d >>> 22) | (d << 10)); - s1 = ((h >>> 6) | (h << 26)) ^ ((h >>> 11) | (h << 21)) ^ ((h >>> 25) | (h << 7)); - da = d & a; - maj = da ^ (d & b) ^ ab; - ch = (h & e) ^ (~h & f); - t1 = g + s1 + ch + K[j + 1] + blocks[j + 1]; - t2 = s0 + maj; - g = c + t1 << 0; - c = t1 + t2 << 0; - s0 = ((c >>> 2) | (c << 30)) ^ ((c >>> 13) | (c << 19)) ^ ((c >>> 22) | (c << 10)); - s1 = ((g >>> 6) | (g << 26)) ^ ((g >>> 11) | (g << 21)) ^ ((g >>> 25) | (g << 7)); - cd = c & d; - maj = cd ^ (c & a) ^ da; - ch = (g & h) ^ (~g & e); - t1 = f + s1 + ch + K[j + 2] + blocks[j + 2]; - t2 = s0 + maj; - f = b + t1 << 0; - b = t1 + t2 << 0; - s0 = ((b >>> 2) | (b << 30)) ^ ((b >>> 13) | (b << 19)) ^ ((b >>> 22) | (b << 10)); - s1 = ((f >>> 6) | (f << 26)) ^ ((f >>> 11) | (f << 21)) ^ ((f >>> 25) | (f << 7)); - bc = b & c; - maj = bc ^ (b & d) ^ cd; - ch = (f & g) ^ (~f & h); - t1 = e + s1 + ch + K[j + 3] + blocks[j + 3]; - t2 = s0 + maj; - e = a + t1 << 0; - a = t1 + t2 << 0; - this.chromeBugWorkAround = true; - } - - this.h0 = this.h0 + a << 0; - this.h1 = this.h1 + b << 0; - this.h2 = this.h2 + c << 0; - this.h3 = this.h3 + d << 0; - this.h4 = this.h4 + e << 0; - this.h5 = this.h5 + f << 0; - this.h6 = this.h6 + g << 0; - this.h7 = this.h7 + h << 0; - }; - - Sha256.prototype.hex = function () { - this.finalize(); - - var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5, - h6 = this.h6, h7 = this.h7; - - var hex = HEX_CHARS[(h0 >>> 28) & 0x0F] + HEX_CHARS[(h0 >>> 24) & 0x0F] + - HEX_CHARS[(h0 >>> 20) & 0x0F] + HEX_CHARS[(h0 >>> 16) & 0x0F] + - HEX_CHARS[(h0 >>> 12) & 0x0F] + HEX_CHARS[(h0 >>> 8) & 0x0F] + - HEX_CHARS[(h0 >>> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] + - HEX_CHARS[(h1 >>> 28) & 0x0F] + HEX_CHARS[(h1 >>> 24) & 0x0F] + - HEX_CHARS[(h1 >>> 20) & 0x0F] + HEX_CHARS[(h1 >>> 16) & 0x0F] + - HEX_CHARS[(h1 >>> 12) & 0x0F] + HEX_CHARS[(h1 >>> 8) & 0x0F] + - HEX_CHARS[(h1 >>> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] + - HEX_CHARS[(h2 >>> 28) & 0x0F] + HEX_CHARS[(h2 >>> 24) & 0x0F] + - HEX_CHARS[(h2 >>> 20) & 0x0F] + HEX_CHARS[(h2 >>> 16) & 0x0F] + - HEX_CHARS[(h2 >>> 12) & 0x0F] + HEX_CHARS[(h2 >>> 8) & 0x0F] + - HEX_CHARS[(h2 >>> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] + - HEX_CHARS[(h3 >>> 28) & 0x0F] + HEX_CHARS[(h3 >>> 24) & 0x0F] + - HEX_CHARS[(h3 >>> 20) & 0x0F] + HEX_CHARS[(h3 >>> 16) & 0x0F] + - HEX_CHARS[(h3 >>> 12) & 0x0F] + HEX_CHARS[(h3 >>> 8) & 0x0F] + - HEX_CHARS[(h3 >>> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] + - HEX_CHARS[(h4 >>> 28) & 0x0F] + HEX_CHARS[(h4 >>> 24) & 0x0F] + - HEX_CHARS[(h4 >>> 20) & 0x0F] + HEX_CHARS[(h4 >>> 16) & 0x0F] + - HEX_CHARS[(h4 >>> 12) & 0x0F] + HEX_CHARS[(h4 >>> 8) & 0x0F] + - HEX_CHARS[(h4 >>> 4) & 0x0F] + HEX_CHARS[h4 & 0x0F] + - HEX_CHARS[(h5 >>> 28) & 0x0F] + HEX_CHARS[(h5 >>> 24) & 0x0F] + - HEX_CHARS[(h5 >>> 20) & 0x0F] + HEX_CHARS[(h5 >>> 16) & 0x0F] + - HEX_CHARS[(h5 >>> 12) & 0x0F] + HEX_CHARS[(h5 >>> 8) & 0x0F] + - HEX_CHARS[(h5 >>> 4) & 0x0F] + HEX_CHARS[h5 & 0x0F] + - HEX_CHARS[(h6 >>> 28) & 0x0F] + HEX_CHARS[(h6 >>> 24) & 0x0F] + - HEX_CHARS[(h6 >>> 20) & 0x0F] + HEX_CHARS[(h6 >>> 16) & 0x0F] + - HEX_CHARS[(h6 >>> 12) & 0x0F] + HEX_CHARS[(h6 >>> 8) & 0x0F] + - HEX_CHARS[(h6 >>> 4) & 0x0F] + HEX_CHARS[h6 & 0x0F]; - if (!this.is224) { - hex += HEX_CHARS[(h7 >>> 28) & 0x0F] + HEX_CHARS[(h7 >>> 24) & 0x0F] + - HEX_CHARS[(h7 >>> 20) & 0x0F] + HEX_CHARS[(h7 >>> 16) & 0x0F] + - HEX_CHARS[(h7 >>> 12) & 0x0F] + HEX_CHARS[(h7 >>> 8) & 0x0F] + - HEX_CHARS[(h7 >>> 4) & 0x0F] + HEX_CHARS[h7 & 0x0F]; - } - return hex; - }; - - Sha256.prototype.toString = Sha256.prototype.hex; - - Sha256.prototype.digest = function () { - this.finalize(); - - var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5, - h6 = this.h6, h7 = this.h7; - - var arr = [ - (h0 >>> 24) & 0xFF, (h0 >>> 16) & 0xFF, (h0 >>> 8) & 0xFF, h0 & 0xFF, - (h1 >>> 24) & 0xFF, (h1 >>> 16) & 0xFF, (h1 >>> 8) & 0xFF, h1 & 0xFF, - (h2 >>> 24) & 0xFF, (h2 >>> 16) & 0xFF, (h2 >>> 8) & 0xFF, h2 & 0xFF, - (h3 >>> 24) & 0xFF, (h3 >>> 16) & 0xFF, (h3 >>> 8) & 0xFF, h3 & 0xFF, - (h4 >>> 24) & 0xFF, (h4 >>> 16) & 0xFF, (h4 >>> 8) & 0xFF, h4 & 0xFF, - (h5 >>> 24) & 0xFF, (h5 >>> 16) & 0xFF, (h5 >>> 8) & 0xFF, h5 & 0xFF, - (h6 >>> 24) & 0xFF, (h6 >>> 16) & 0xFF, (h6 >>> 8) & 0xFF, h6 & 0xFF - ]; - if (!this.is224) { - arr.push((h7 >>> 24) & 0xFF, (h7 >>> 16) & 0xFF, (h7 >>> 8) & 0xFF, h7 & 0xFF); - } - return arr; - }; - - Sha256.prototype.array = Sha256.prototype.digest; - - Sha256.prototype.arrayBuffer = function () { - this.finalize(); - - var buffer = new ArrayBuffer(this.is224 ? 28 : 32); - var dataView = new DataView(buffer); - dataView.setUint32(0, this.h0); - dataView.setUint32(4, this.h1); - dataView.setUint32(8, this.h2); - dataView.setUint32(12, this.h3); - dataView.setUint32(16, this.h4); - dataView.setUint32(20, this.h5); - dataView.setUint32(24, this.h6); - if (!this.is224) { - dataView.setUint32(28, this.h7); - } - return buffer; - }; - - function HmacSha256(key, is224, sharedMemory) { - var i, type = typeof key; - if (type === 'string') { - var bytes = [], length = key.length, index = 0, code; - for (i = 0; i < length; ++i) { - code = key.charCodeAt(i); - if (code < 0x80) { - bytes[index++] = code; - } else if (code < 0x800) { - bytes[index++] = (0xc0 | (code >>> 6)); - bytes[index++] = (0x80 | (code & 0x3f)); - } else if (code < 0xd800 || code >= 0xe000) { - bytes[index++] = (0xe0 | (code >>> 12)); - bytes[index++] = (0x80 | ((code >>> 6) & 0x3f)); - bytes[index++] = (0x80 | (code & 0x3f)); - } else { - code = 0x10000 + (((code & 0x3ff) << 10) | (key.charCodeAt(++i) & 0x3ff)); - bytes[index++] = (0xf0 | (code >>> 18)); - bytes[index++] = (0x80 | ((code >>> 12) & 0x3f)); - bytes[index++] = (0x80 | ((code >>> 6) & 0x3f)); - bytes[index++] = (0x80 | (code & 0x3f)); - } - } - key = bytes; - } else { - if (type === 'object') { - if (key === null) { - throw new Error(ERROR); - } else if (ARRAY_BUFFER && key.constructor === ArrayBuffer) { - key = new Uint8Array(key); - } else if (!Array.isArray(key)) { - if (!ARRAY_BUFFER || !ArrayBuffer.isView(key)) { - throw new Error(ERROR); - } - } - } else { - throw new Error(ERROR); - } - } - - if (key.length > 64) { - key = (new Sha256(is224, true)).update(key).array(); - } - - var oKeyPad = [], iKeyPad = []; - for (i = 0; i < 64; ++i) { - var b = key[i] || 0; - oKeyPad[i] = 0x5c ^ b; - iKeyPad[i] = 0x36 ^ b; - } - - Sha256.call(this, is224, sharedMemory); - - this.update(iKeyPad); - this.oKeyPad = oKeyPad; - this.inner = true; - this.sharedMemory = sharedMemory; - } - HmacSha256.prototype = new Sha256(); - - HmacSha256.prototype.finalize = function () { - Sha256.prototype.finalize.call(this); - if (this.inner) { - this.inner = false; - var innerHash = this.array(); - Sha256.call(this, this.is224, this.sharedMemory); - this.update(this.oKeyPad); - this.update(innerHash); - Sha256.prototype.finalize.call(this); - } - }; - - var exports = createMethod(); - exports.sha256 = exports; - exports.sha224 = createMethod(true); - exports.sha256.hmac = createHmacMethod(); - exports.sha224.hmac = createHmacMethod(true); - - if (COMMON_JS) { - module.exports = exports; - } else { - root.sha256 = exports.sha256; - root.sha224 = exports.sha224; - if (AMD) { - define(function () { - return exports; - }); - } - } -})(); - -async function getAccountId(email, key) { - try { - const url = 'https://api.cloudflare.com/client/v4/accounts'; - const headers = new Headers({ - 'X-AUTH-EMAIL': email, - 'X-AUTH-KEY': key - }); - const response = await fetch(url, { headers }); - const data = await response.json(); - return data.result[0].id; // 假设我们需要第一个账号ID - } catch (error) { - return false ; - } -} - -async function getSum(accountId, accountIndex, email, key, startDate, endDate) { - try { - const startDateISO = new Date(startDate).toISOString(); - const endDateISO = new Date(endDate).toISOString(); - - const query = JSON.stringify({ - query: `query getBillingMetrics($accountId: String!, $filter: AccountWorkersInvocationsAdaptiveFilter_InputObject) { - viewer { - accounts(filter: {accountTag: $accountId}) { - pagesFunctionsInvocationsAdaptiveGroups(limit: 1000, filter: $filter) { - sum { - requests - } - } - workersInvocationsAdaptive(limit: 10000, filter: $filter) { - sum { - requests - } - } - } - } - }`, - variables: { - accountId, - filter: { datetime_geq: startDateISO, datetime_leq: endDateISO } - }, - }); - - const headers = new Headers({ - 'Content-Type': 'application/json', - 'X-AUTH-EMAIL': email, - 'X-AUTH-KEY': key, - }); - - const response = await fetch(`https://api.cloudflare.com/client/v4/graphql`, { - method: 'POST', - headers: headers, - body: query - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const res = await response.json(); - - const pagesFunctionsInvocationsAdaptiveGroups = res?.data?.viewer?.accounts?.[accountIndex]?.pagesFunctionsInvocationsAdaptiveGroups; - const workersInvocationsAdaptive = res?.data?.viewer?.accounts?.[accountIndex]?.workersInvocationsAdaptive; - - if (!pagesFunctionsInvocationsAdaptiveGroups && !workersInvocationsAdaptive) { - throw new Error('找不到数据'); - } - - const pagesSum = pagesFunctionsInvocationsAdaptiveGroups.reduce((a, b) => a + b?.sum.requests, 0); - const workersSum = workersInvocationsAdaptive.reduce((a, b) => a + b?.sum.requests, 0); - - //console.log(`范围: ${startDateISO} ~ ${endDateISO}\n默认取第 ${accountIndex} 项`); - - return [pagesSum, workersSum ]; - } catch (error) { - return [ 0, 0 ]; - } -} - -/** - * - * @param {number} addressType - * @param {string} addressRemote - * @param {number} portRemote - * @param {function} log The logging function. - */ -async function socks5Connect(addressType, addressRemote, portRemote, log) { - const { username, password, hostname, port } = parsedSocks5Address; - // Connect to the SOCKS server - const socket = connect({ - hostname, - port, - }); - - // Request head format (Worker -> Socks Server): - // +----+----------+----------+ - // |VER | NMETHODS | METHODS | - // +----+----------+----------+ - // | 1 | 1 | 1 to 255 | - // +----+----------+----------+ - - // https://en.wikipedia.org/wiki/SOCKS#SOCKS5 - // For METHODS: - // 0x00 NO AUTHENTICATION REQUIRED - // 0x02 USERNAME/PASSWORD https://datatracker.ietf.org/doc/html/rfc1929 - const socksGreeting = new Uint8Array([5, 2, 0, 2]); - - const writer = socket.writable.getWriter(); - - await writer.write(socksGreeting); - log('sent socks greeting'); - - const reader = socket.readable.getReader(); - const encoder = new TextEncoder(); - let res = (await reader.read()).value; - // Response format (Socks Server -> Worker): - // +----+--------+ - // |VER | METHOD | - // +----+--------+ - // | 1 | 1 | - // +----+--------+ - if (res[0] !== 0x05) { - log(`socks server version error: ${res[0]} expected: 5`); - return; - } - if (res[1] === 0xff) { - log("no acceptable methods"); - return; - } - - // if return 0x0502 - if (res[1] === 0x02) { - log("socks server needs auth"); - if (!username || !password) { - log("please provide username/password"); - return; - } - // +----+------+----------+------+----------+ - // |VER | ULEN | UNAME | PLEN | PASSWD | - // +----+------+----------+------+----------+ - // | 1 | 1 | 1 to 255 | 1 | 1 to 255 | - // +----+------+----------+------+----------+ - const authRequest = new Uint8Array([ - 1, - username.length, - ...encoder.encode(username), - password.length, - ...encoder.encode(password) - ]); - await writer.write(authRequest); - res = (await reader.read()).value; - // expected 0x0100 - if (res[0] !== 0x01 || res[1] !== 0x00) { - log("fail to auth socks server"); - return; - } - } - - // Request data format (Worker -> Socks Server): - // +----+-----+-------+------+----------+----------+ - // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | - // +----+-----+-------+------+----------+----------+ - // | 1 | 1 | X'00' | 1 | Variable | 2 | - // +----+-----+-------+------+----------+----------+ - // ATYP: address type of following address - // 0x01: IPv4 address - // 0x03: Domain name - // 0x04: IPv6 address - // DST.ADDR: desired destination address - // DST.PORT: desired destination port in network octet order - - // addressType - // 0x01: IPv4 address - // 0x03: Domain name - // 0x04: IPv6 address - // 1--> ipv4 addressLength =4 - // 2--> domain name - // 3--> ipv6 addressLength =16 - let DSTADDR; // DSTADDR = ATYP + DST.ADDR - switch (addressType) { - case 1: - DSTADDR = new Uint8Array( - [1, ...addressRemote.split('.').map(Number)] - ); - break; - case 3: - DSTADDR = new Uint8Array( - [3, addressRemote.length, ...encoder.encode(addressRemote)] - ); - break; - case 4: - DSTADDR = new Uint8Array( - [4, ...addressRemote.split(':').flatMap(x => [parseInt(x.slice(0, 2), 16), parseInt(x.slice(2), 16)])] - ); - break; - default: - log(`invild addressType is ${addressType}`); - return; - } - const socksRequest = new Uint8Array([5, 1, 0, ...DSTADDR, portRemote >> 8, portRemote & 0xff]); - await writer.write(socksRequest); - log('sent socks request'); - - res = (await reader.read()).value; - // Response format (Socks Server -> Worker): - // +----+-----+-------+------+----------+----------+ - // |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | - // +----+-----+-------+------+----------+----------+ - // | 1 | 1 | X'00' | 1 | Variable | 2 | - // +----+-----+-------+------+----------+----------+ - if (res[1] === 0x00) { - log("socks connection opened"); - } else { - log("fail to open socks connection"); - return; - } - writer.releaseLock(); - reader.releaseLock(); - return socket; -} - - -/** - * - * @param {string} address - */ -function socks5AddressParser(address) { - let [latter, former] = address.split("@").reverse(); - let username, password, hostname, port; - if (former) { - const formers = former.split(":"); - if (formers.length !== 2) { - throw new Error('Invalid SOCKS address format'); - } - [username, password] = formers; - } - const latters = latter.split(":"); - port = Number(latters.pop()); - if (isNaN(port)) { - throw new Error('Invalid SOCKS address format'); - } - hostname = latters.join(":"); - const regex = /^\[.*\]$/; - if (hostname.includes(":") && !regex.test(hostname)) { - throw new Error('Invalid SOCKS address format'); - } - //if (/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(hostname)) hostname = `${atob('d3d3Lg==')}${hostname}${atob('LmlwLjA5MDIyNy54eXo=')}`; - return { - username, - password, - hostname, - port, - } -} - -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); -} +// _worker.src.js +import { connect } from "cloudflare:sockets"; +let password = 'auto'; +let proxyIP = ''; +// The user name and password do not contain special characters +// Setting the address will ignore proxyIP +// Example: user:pass@host:port or host:port +let socks5Address = ''; + +let addresses = [ + //当sub为空时启用本地优选域名/优选IP,若不带端口号 TLS默认端口为443,#号后为备注别名 +]; + +let sub = ''; +let subconverter = 'SUBAPI.fxxk.dedyn.io';// clash订阅转换后端,目前使用CM的订阅转换功能。自带虚假节点信息防泄露 +let subconfig = "https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online_Mini_MultiMode.ini"; //订阅配置文件 +let subProtocol = 'https'; +let RproxyIP = 'false'; + +let addressesapi = []; +let addressescsv = []; +let DLS = 8; + +let FileName = 'epeius'; +let BotToken =''; +let ChatID =''; +let proxyhosts = [];//本地代理域名池 +let proxyhostsURL = ''; +let go2Socks5s = [ + '*ttvnw.net', + '*tapecontent.net', + '*cloudatacdn.com', + '*.loadshare.org', +]; + +let fakeUserID ; +let fakeHostName ; +let proxyIPs ; +let socks5s; +let sha224Password ; +const expire = 4102329600;//2099-12-31 +const regex = /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[.*\]):?(\d+)?#?(.*)?$/; + +let parsedSocks5Address = {}; +let enableSocks = false; +let httpsPorts = ["2053","2083","2087","2096","8443"]; +/*Obfuscate-cmliu*/ +/* +if (!isValidSHA224(sha224Password)) { + throw new Error('sha224Password is not valid'); +} +*/ +export default { + async fetch(request, env, ctx) { + try { + const UA = request.headers.get('User-Agent') || 'null'; + const userAgent = UA.toLowerCase(); + password = env.PASSWORD || password; + sha224Password = env.SHA224 || env.SHA224PASS || sha256.sha224(password); + //console.log(sha224Password); + + const currentDate = new Date(); + currentDate.setHours(0, 0, 0, 0); // 设置时间为当天 + const timestamp = Math.ceil(currentDate.getTime() / 1000); + const fakeUserIDMD5 = await MD5MD5(`${password}${timestamp}`); + 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(fakeUserID); // 打印fakeID + + proxyIP = env.PROXYIP || proxyIP; + proxyIPs = await ADD(proxyIP); + proxyIP = proxyIPs[Math.floor(Math.random() * proxyIPs.length)]; + socks5Address = env.SOCKS5 || socks5Address; + socks5s = await ADD(socks5Address); + socks5Address = socks5s[Math.floor(Math.random() * socks5s.length)]; + socks5Address = socks5Address.split('//')[1] || socks5Address; + if (env.CFPORTS) httpsPorts = await ADD(env.CFPORTS); + sub = env.SUB || sub; + subconverter = env.SUBAPI || subconverter; + if( subconverter.includes("http://") ){ + subconverter = subconverter.split("//")[1]; + subProtocol = 'http'; + } else { + subconverter = subconverter.split("//")[1] || subconverter; + } + subconfig = env.SUBCONFIG || subconfig; + if (socks5Address) { + try { + parsedSocks5Address = socks5AddressParser(socks5Address); + RproxyIP = env.RPROXYIP || 'false'; + enableSocks = true; + } catch (err) { + /** @type {Error} */ + let e = err; + console.log(e.toString()); + RproxyIP = env.RPROXYIP || !proxyIP ? 'true' : 'false'; + enableSocks = false; + } + } else { + RproxyIP = env.RPROXYIP || !proxyIP ? 'true' : 'false'; + } + if (env.ADD) addresses = await ADD(env.ADD); + if (env.ADDAPI) addressesapi = await ADD(env.ADDAPI); + if (env.ADDCSV) addressescsv = await ADD(env.ADDCSV); + DLS = env.DLS || DLS; + BotToken = env.TGTOKEN || BotToken; + ChatID = env.TGID || ChatID; + if(env.GO2SOCKS5) go2Socks5s = await ADD(env.GO2SOCKS5); + const upgradeHeader = request.headers.get("Upgrade"); + const url = new URL(request.url); + if (url.searchParams.has('sub') && url.searchParams.get('sub') !== '') sub = url.searchParams.get('sub'); + FileName = env.SUBNAME || FileName; + if (!upgradeHeader || upgradeHeader !== "websocket") { + //const url = new URL(request.url); + switch (url.pathname) { + case '/': + 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), { + status: 200, + headers: { + 'content-type': 'application/json', + }, + }); + case `/${fakeUserID}`: + const fakeConfig = await getTrojanConfig(password, request.headers.get('Host'), sub, 'CF-Workers-SUB', RproxyIP, url); + return new Response(`${fakeConfig}`, { status: 200 }); + case `/${password}`: + await sendMessage(`#获取订阅 ${FileName}`, request.headers.get('CF-Connecting-IP'), `UA: ${UA}\n域名: ${url.hostname}\n入口: ${url.pathname + url.search}`); + const trojanConfig = await getTrojanConfig(password, request.headers.get('Host'), sub, UA, RproxyIP, url); + const now = Date.now(); + //const timestamp = Math.floor(now / 1000); + const today = new Date(now); + today.setHours(0, 0, 0, 0); + const UD = Math.floor(((now - today.getTime())/86400000) * 24 * 1099511627776 / 2); + let pagesSum = UD; + let workersSum = UD; + let total = 24 * 1099511627776 ; + if (env.CFEMAIL && env.CFKEY){ + const email = env.CFEMAIL; + const key = env.CFKEY; + const accountIndex = env.CFID || 0; + const accountId = await getAccountId(email, key); + if (accountId){ + const now = new Date() + now.setUTCHours(0, 0, 0, 0) + const startDate = now.toISOString() + const endDate = new Date().toISOString(); + const Sum = await getSum(accountId, accountIndex, email, key, startDate, endDate); + pagesSum = Sum[0]; + workersSum = Sum[1]; + total = 102400 ; + } + } + //console.log(`pagesSum: ${pagesSum}\nworkersSum: ${workersSum}\ntotal: ${total}`); + if (userAgent && (userAgent.includes('mozilla') || userAgent.includes('subconverter'))){ + return new Response(`${trojanConfig}`, { + status: 200, + headers: { + "Content-Type": "text/plain;charset=utf-8", + "Profile-Update-Interval": "6", + "Subscription-Userinfo": `upload=${pagesSum}; download=${workersSum}; total=${total}; expire=${expire}`, + } + }); + } else { + return new Response(`${trojanConfig}`, { + status: 200, + headers: { + "Content-Disposition": `attachment; filename=${FileName}; filename*=utf-8''${encodeURIComponent(FileName)}`, + "Content-Type": "text/plain;charset=utf-8", + "Profile-Update-Interval": "6", + "Subscription-Userinfo": `upload=${pagesSum}; download=${workersSum}; total=${total}; expire=${expire}`, + } + }); + } + default: + if (env.URL302) return Response.redirect(env.URL302, 302); + else if (env.URL) return await proxyURL(env.URL, url); + else return new Response('不用怀疑!你PASSWORD就是错的!!!', { status: 404 }); + } + } else { + proxyIP = url.searchParams.get('proxyip') || proxyIP; + if (new RegExp('/proxyip=', 'i').test(url.pathname)) proxyIP = url.pathname.toLowerCase().split('/proxyip=')[1]; + else if (new RegExp('/proxyip.', 'i').test(url.pathname)) proxyIP = `proxyip.${url.pathname.toLowerCase().split("/proxyip.")[1]}`; + + socks5Address = url.searchParams.get('socks5') || socks5Address; + if (new RegExp('/socks5=', 'i').test(url.pathname)) socks5Address = url.pathname.split('5=')[1]; + else if (new RegExp('/socks://', 'i').test(url.pathname) || new RegExp('/socks5://', 'i').test(url.pathname)) { + socks5Address = url.pathname.split('://')[1].split('#')[0]; + if (socks5Address.includes('@')){ + let userPassword = socks5Address.split('@')[0]; + const base64Regex = /^(?:[A-Z0-9+/]{4})*(?:[A-Z0-9+/]{2}==|[A-Z0-9+/]{3}=)?$/i; + if (base64Regex.test(userPassword) && !userPassword.includes(':')) userPassword = atob(userPassword); + socks5Address = `${userPassword}@${socks5Address.split('@')[1]}`; + } + } + if (socks5Address) { + try { + parsedSocks5Address = socks5AddressParser(socks5Address); + enableSocks = true; + } catch (err) { + /** @type {Error} */ + let e = err; + console.log(e.toString()); + enableSocks = false; + } + } else { + enableSocks = false; + } + return await trojanOverWSHandler(request); + } + } catch (err) { + let e = err; + return new Response(e.toString()); + } + } +}; + +async function trojanOverWSHandler(request) { + const webSocketPair = new WebSocketPair(); + const [client, webSocket] = Object.values(webSocketPair); + webSocket.accept(); + let address = ""; + let portWithRandomLog = ""; + const log = (info, event) => { + console.log(`[${address}:${portWithRandomLog}] ${info}`, event || ""); + }; + const earlyDataHeader = request.headers.get("sec-websocket-protocol") || ""; + const readableWebSocketStream = makeReadableWebSocketStream(webSocket, earlyDataHeader, log); + let remoteSocketWapper = { + value: null + }; + let udpStreamWrite = null; + readableWebSocketStream.pipeTo(new WritableStream({ + async write(chunk, controller) { + if (udpStreamWrite) { + return udpStreamWrite(chunk); + } + if (remoteSocketWapper.value) { + const writer = remoteSocketWapper.value.writable.getWriter(); + await writer.write(chunk); + writer.releaseLock(); + return; + } + const { + hasError, + message, + portRemote = 443, + addressRemote = "", + rawClientData, + addressType + } = await parseTrojanHeader(chunk); + address = addressRemote; + portWithRandomLog = `${portRemote}--${Math.random()} tcp`; + if (hasError) { + throw new Error(message); + return; + } + handleTCPOutBound(remoteSocketWapper, addressRemote, portRemote, rawClientData, webSocket, log, addressType); + }, + close() { + log(`readableWebSocketStream is closed`); + }, + abort(reason) { + log(`readableWebSocketStream is aborted`, JSON.stringify(reason)); + } + })).catch((err) => { + log("readableWebSocketStream pipeTo error", err); + }); + return new Response(null, { + status: 101, + // @ts-ignore + webSocket: client + }); +} + +async function parseTrojanHeader(buffer) { + if (buffer.byteLength < 56) { + return { + hasError: true, + message: "invalid data" + }; + } + let crLfIndex = 56; + if (new Uint8Array(buffer.slice(56, 57))[0] !== 0x0d || new Uint8Array(buffer.slice(57, 58))[0] !== 0x0a) { + return { + hasError: true, + message: "invalid header format (missing CR LF)" + }; + } + const password = new TextDecoder().decode(buffer.slice(0, crLfIndex)); + if (password !== sha224Password) { + return { + hasError: true, + message: "invalid password" + }; + } + + const socks5DataBuffer = buffer.slice(crLfIndex + 2); + if (socks5DataBuffer.byteLength < 6) { + return { + hasError: true, + message: "invalid SOCKS5 request data" + }; + } + + const view = new DataView(socks5DataBuffer); + const cmd = view.getUint8(0); + if (cmd !== 1) { + return { + hasError: true, + message: "unsupported command, only TCP (CONNECT) is allowed" + }; + } + + const atype = view.getUint8(1); + // 0x01: IPv4 address + // 0x03: Domain name + // 0x04: IPv6 address + let addressLength = 0; + let addressIndex = 2; + let address = ""; + switch (atype) { + case 1: + addressLength = 4; + address = new Uint8Array( + socks5DataBuffer.slice(addressIndex, addressIndex + addressLength) + ).join("."); + break; + case 3: + addressLength = new Uint8Array( + socks5DataBuffer.slice(addressIndex, addressIndex + 1) + )[0]; + addressIndex += 1; + address = new TextDecoder().decode( + socks5DataBuffer.slice(addressIndex, addressIndex + addressLength) + ); + break; + case 4: + addressLength = 16; + const dataView = new DataView(socks5DataBuffer.slice(addressIndex, addressIndex + addressLength)); + const ipv6 = []; + for (let i = 0; i < 8; i++) { + ipv6.push(dataView.getUint16(i * 2).toString(16)); + } + address = ipv6.join(":"); + break; + default: + return { + hasError: true, + message: `invalid addressType is ${atype}` + }; + } + + if (!address) { + return { + hasError: true, + message: `address is empty, addressType is ${atype}` + }; + } + + const portIndex = addressIndex + addressLength; + const portBuffer = socks5DataBuffer.slice(portIndex, portIndex + 2); + const portRemote = new DataView(portBuffer).getUint16(0); + return { + hasError: false, + addressRemote: address, + portRemote, + rawClientData: socks5DataBuffer.slice(portIndex + 4), + addressType: atype + }; +} + +async function handleTCPOutBound(remoteSocket, addressRemote, portRemote, rawClientData, webSocket, log, addressType) { + async function useSocks5Pattern(address) { + if ( go2Socks5s.includes(atob('YWxsIGlu')) || go2Socks5s.includes(atob('Kg==')) ) return true; + return go2Socks5s.some(pattern => { + let regexPattern = pattern.replace(/\*/g, '.*'); + let regex = new RegExp(`^${regexPattern}$`, 'i'); + return regex.test(address); + }); + } + async function connectAndWrite(address, port, socks = false) { + log(`connected to ${address}:${port}`); + //if (/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(address)) address = `${atob('d3d3Lg==')}${address}${atob('LmlwLjA5MDIyNy54eXo=')}`; + const tcpSocket = socks ? await socks5Connect(addressType, address, port, log) + : connect({ + hostname: address, + port + }); + remoteSocket.value = tcpSocket; + //log(`connected to ${address}:${port}`); + const writer = tcpSocket.writable.getWriter(); + await writer.write(rawClientData); + writer.releaseLock(); + return tcpSocket; + } + async function retry() { + if (enableSocks) { + tcpSocket = await connectAndWrite(addressRemote, portRemote, true); + } else { + if (!proxyIP || proxyIP == '') { + proxyIP = atob('UFJPWFlJUC50cDEuZnh4ay5kZWR5bi5pbw=='); + } else if (proxyIP.includes(']:')) { + portRemote = proxyIP.split(']:')[1] || portRemote; + proxyIP = proxyIP.split(']:')[0] || proxyIP; + } else if (proxyIP.split(':').length === 2) { + 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); + } + tcpSocket.closed.catch((error) => { + console.log("retry tcpSocket closed error", error); + }).finally(() => { + safeCloseWebSocket(webSocket); + }); + remoteSocketToWS(tcpSocket, webSocket, null, log); + } + let useSocks = false; + if( go2Socks5s.length > 0 && enableSocks ) useSocks = await useSocks5Pattern(addressRemote); + let tcpSocket = await connectAndWrite(addressRemote, portRemote, useSocks); + remoteSocketToWS(tcpSocket, webSocket, retry, log); +} + +function makeReadableWebSocketStream(webSocketServer, earlyDataHeader, log) { + let readableStreamCancel = false; + const stream = new ReadableStream({ + start(controller) { + webSocketServer.addEventListener("message", (event) => { + if (readableStreamCancel) { + return; + } + const message = event.data; + controller.enqueue(message); + }); + webSocketServer.addEventListener("close", () => { + safeCloseWebSocket(webSocketServer); + if (readableStreamCancel) { + return; + } + controller.close(); + }); + webSocketServer.addEventListener("error", (err) => { + log("webSocketServer error"); + controller.error(err); + }); + const { earlyData, error } = base64ToArrayBuffer(earlyDataHeader); + if (error) { + controller.error(error); + } else if (earlyData) { + controller.enqueue(earlyData); + } + }, + pull(controller) {}, + cancel(reason) { + if (readableStreamCancel) { + return; + } + log(`readableStream was canceled, due to ${reason}`); + readableStreamCancel = true; + safeCloseWebSocket(webSocketServer); + } + }); + return stream; +} + +async function remoteSocketToWS(remoteSocket, webSocket, retry, log) { + let hasIncomingData = false; + await remoteSocket.readable.pipeTo( + new WritableStream({ + start() {}, + /** + * + * @param {Uint8Array} chunk + * @param {*} controller + */ + async write(chunk, controller) { + hasIncomingData = true; + if (webSocket.readyState !== WS_READY_STATE_OPEN) { + controller.error( + "webSocket connection is not open" + ); + } + webSocket.send(chunk); + }, + close() { + log(`remoteSocket.readable is closed, hasIncomingData: ${hasIncomingData}`); + }, + abort(reason) { + console.error("remoteSocket.readable abort", reason); + } + }) + ).catch((error) => { + console.error( + `remoteSocketToWS error:`, + error.stack || error + ); + safeCloseWebSocket(webSocket); + }); + if (hasIncomingData === false && retry) { + log(`retry`); + retry(); + } +} +/* +function isValidSHA224(hash) { + const sha224Regex = /^[0-9a-f]{56}$/i; + return sha224Regex.test(hash); +} +*/ +function base64ToArrayBuffer(base64Str) { + if (!base64Str) { + return { error: null }; + } + try { + base64Str = base64Str.replace(/-/g, "+").replace(/_/g, "/"); + const decode = atob(base64Str); + const arryBuffer = Uint8Array.from(decode, (c) => c.charCodeAt(0)); + return { earlyData: arryBuffer.buffer, error: null }; + } catch (error) { + return { error }; + } +} + +let WS_READY_STATE_OPEN = 1; +let WS_READY_STATE_CLOSING = 2; + +function safeCloseWebSocket(socket) { + try { + if (socket.readyState === WS_READY_STATE_OPEN || socket.readyState === WS_READY_STATE_CLOSING) { + socket.close(); + } + } catch (error) { + console.error("safeCloseWebSocket error", error); + } +} + +/* +export { + worker_default as + default +}; +//# sourceMappingURL=worker.js.map +*/ + +function revertFakeInfo(content, userID, hostName, isBase64) { + if (isBase64) content = atob(content);//Base64解码 + content = content.replace(new RegExp(fakeUserID, 'g'), userID).replace(new RegExp(fakeHostName, 'g'), hostName); + //console.log(content); + if (isBase64) content = btoa(content);//Base64编码 + + return content; +} + +async function MD5MD5(text) { + const encoder = new TextEncoder(); + + const firstPass = await crypto.subtle.digest('MD5', encoder.encode(text)); + const firstPassArray = Array.from(new Uint8Array(firstPass)); + const firstHex = firstPassArray.map(b => b.toString(16).padStart(2, '0')).join(''); + + const secondPass = await crypto.subtle.digest('MD5', encoder.encode(firstHex.slice(7, 27))); + const secondPassArray = Array.from(new Uint8Array(secondPass)); + const secondHex = secondPassArray.map(b => b.toString(16).padStart(2, '0')).join(''); + + return secondHex.toLowerCase(); +} + +async function ADD(内容) { + // 将制表符、双引号、单引号和换行符都替换为逗号 + // 然后将连续的多个逗号替换为单个逗号 + var 替换后的内容 = 内容.replace(/[ |"'\r\n]+/g, ',').replace(/,+/g, ','); + + // 删除开头和结尾的逗号(如果有的话) + if (替换后的内容.charAt(0) == ',') 替换后的内容 = 替换后的内容.slice(1); + if (替换后的内容.charAt(替换后的内容.length - 1) == ',') 替换后的内容 = 替换后的内容.slice(0, 替换后的内容.length - 1); + + // 使用逗号分割字符串,得到地址数组 + const 地址数组 = 替换后的内容.split(','); + + return 地址数组; +} + +async function proxyURL(proxyURL, url) { + const URLs = await ADD(proxyURL); + const fullURL = URLs[Math.floor(Math.random() * URLs.length)]; + // 解析目标 URL + let parsedURL = new URL(fullURL); + console.log(parsedURL); + // 提取并可能修改 URL 组件 + let URLProtocol = parsedURL.protocol.slice(0, -1) || 'https'; + let URLHostname = parsedURL.hostname; + let URLPathname = parsedURL.pathname; + let URLSearch = parsedURL.search; + // 处理 pathname + if (URLPathname.charAt(URLPathname.length - 1) == '/') { + URLPathname = URLPathname.slice(0, -1); + } + URLPathname += url.pathname; + // 构建新的 URL + let newURL = `${URLProtocol}://${URLHostname}${URLPathname}${URLSearch}`; + // 反向代理请求 + let response = await fetch(newURL); + // 创建新的响应 + let newResponse = new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers: response.headers + }); + // 添加自定义头部,包含 URL 信息 + //newResponse.headers.set('X-Proxied-By', 'Cloudflare Worker'); + //newResponse.headers.set('X-Original-URL', fullURL); + newResponse.headers.set('X-New-URL', newURL); + return newResponse; +} + +function checkSUB() { + // 检查是否没有订阅且所有地址数组都为空 + if ((!sub || sub == '') && (addresses.length + addressesapi.length + addressescsv.length) == 0) { + // 定义 Cloudflare IP 范围的 CIDR 列表 + let cfips = [ + '103.21.244.0/23', + '104.16.0.0/13', + '104.24.0.0/14', + '172.64.0.0/14', + '103.21.244.0/23', + '104.16.0.0/14', + '104.24.0.0/15', + '141.101.64.0/19', + '172.64.0.0/14', + '188.114.96.0/21', + '190.93.240.0/21', + ]; + + // 生成符合给定 CIDR 范围的随机 IP 地址 + function generateRandomIPFromCIDR(cidr) { + const [base, mask] = cidr.split('/'); + const baseIP = base.split('.').map(Number); + const subnetMask = 32 - parseInt(mask, 10); + const maxHosts = Math.pow(2, subnetMask) - 1; + const randomHost = Math.floor(Math.random() * maxHosts); + + const randomIP = baseIP.map((octet, index) => { + if (index < 2) return octet; + if (index === 2) return (octet & (255 << (subnetMask - 8))) + ((randomHost >> 8) & 255); + return (octet & (255 << subnetMask)) + (randomHost & 255); + }); + + return randomIP.join('.'); + } + addresses = addresses.concat('127.0.0.1:1234#CFnat'); + addresses = addresses.concat(cfips.map(cidr => generateRandomIPFromCIDR(cidr) + '#CF随机节点')); + } +} + +function 配置信息(密码, 域名地址) { + const 啥啥啥_写的这是啥啊 = 'dHJvamFu'; + const 协议类型 = atob(啥啥啥_写的这是啥啊); + + const 别名 = FileName; + let 地址 = 域名地址; + let 端口 = 443; + + const 传输层协议 = 'ws'; + const 伪装域名 = 域名地址; + const 路径 = '/?ed=2560'; + + let 传输层安全 = ['tls',true]; + const SNI = 域名地址; + const 指纹 = 'randomized'; + + const v2ray = `${协议类型}://${encodeURIComponent(密码)}@${地址}:${端口}?security=${传输层安全[0]}&sni=${SNI}&alpn=h3&fp=${指纹}&allowInsecure=1&type=${传输层协议}&host=${伪装域名}&path=${encodeURIComponent(路径)}#${encodeURIComponent(别名)}` + const clash = `- {name: ${别名}, server: ${地址}, port: ${端口}, udp: false, client-fingerprint: ${指纹}, type: ${协议类型}, password: ${密码}, sni: ${SNI}, alpn: [h3], skip-cert-verify: true, network: ${传输层协议}, ws-opts: {path: "${路径}", headers: {Host: ${伪装域名}}}}`; + + return [v2ray,clash]; +} + +let subParams = ['sub','base64','b64','clash','singbox','sb','surge']; +async function getTrojanConfig(password, hostName, sub, UA, RproxyIP, _url) { + checkSUB(hostName); + const userAgent = UA.toLowerCase(); + const Config = 配置信息(password , hostName); + const v2ray = Config[0]; + const clash = Config[1]; + let proxyhost = ""; + if(hostName.includes(".workers.dev")){ + if ( proxyhostsURL && (!proxyhosts || proxyhosts.length == 0)) { + try { + const response = await fetch(proxyhostsURL); + + if (!response.ok) { + console.error('获取地址时出错:', response.status, response.statusText); + return; // 如果有错误,直接返回 + } + + const text = await response.text(); + const lines = text.split('\n'); + // 过滤掉空行或只包含空白字符的行 + const nonEmptyLines = lines.filter(line => line.trim() !== ''); + + proxyhosts = proxyhosts.concat(nonEmptyLines); + } catch (error) { + //console.error('获取地址时出错:', error); + } + } + if (proxyhosts.length != 0) proxyhost = proxyhosts[Math.floor(Math.random() * proxyhosts.length)] + "/"; + } + + if ( userAgent.includes('mozilla') && !subParams.some(_searchParams => _url.searchParams.has(_searchParams))) { + let surge = `Surge订阅地址:\nhttps://${proxyhost}${hostName}/${password}?surge`; + if (hostName.includes(".workers.dev") || hostName.includes(".pages.dev")) surge = "Surge订阅必须绑定自定义域"; + const newSocks5s = socks5s.map(socks5Address => { + if (socks5Address.includes('@')) return socks5Address.split('@')[1]; + else if (socks5Address.includes('//')) return socks5Address.split('//')[1]; + else return socks5Address; + }); + + let socks5List = ''; + if( go2Socks5s.length > 0 && enableSocks ) { + socks5List = `${decodeURIComponent('SOCKS5%EF%BC%88%E7%99%BD%E5%90%8D%E5%8D%95%EF%BC%89%3A%20')}`; + if ( go2Socks5s.includes(atob('YWxsIGlu')) || go2Socks5s.includes(atob('Kg==')) ) socks5List += `${decodeURIComponent('%E6%89%80%E6%9C%89%E6%B5%81%E9%87%8F')}\n`; + else socks5List += `\n ${go2Socks5s.join('\n ')}\n`; + } + + let 订阅器 = ''; + if (!sub || sub == '') { + if (enableSocks) 订阅器 += `CFCDN(访问方式): Socks5\n ${newSocks5s.join('\n ')}\n${socks5List}`; + else if (proxyIP && proxyIP != '') 订阅器 += `CFCDN(访问方式): ProxyIP\n ${proxyIPs.join('\n ')}\n`; + else 订阅器 += `CFCDN(访问方式): 无法访问, 需要您设置 proxyIP/PROXYIP !!!\n`; + 订阅器 += `\n您的订阅内容由 内置 addresses/ADD* 参数变量提供\n`; + if (addresses.length > 0) 订阅器 += `ADD(TLS优选域名&IP): \n ${addresses.join('\n ')}\n`; + if (addressesapi.length > 0) 订阅器 += `ADDAPI(TLS优选域名&IP 的 API): \n ${addressesapi.join('\n ')}\n`; + if (addressescsv.length > 0) 订阅器 += `ADDCSV(IPTest测速csv文件 限速 ${DLS} ): \n ${addressescsv.join('\n ')}\n`; + } else { + if (enableSocks) 订阅器 += `CFCDN(访问方式): Socks5\n ${newSocks5s.join('\n ')}\n${socks5List}`; + else if (proxyIP && proxyIP != '') 订阅器 += `CFCDN(访问方式): ProxyIP\n ${proxyIPs.join('\n ')}\n`; + else if (RproxyIP == 'true') 订阅器 += `CFCDN(访问方式): 自动获取ProxyIP\n`; + else 订阅器 += `CFCDN(访问方式): 无法访问, 需要您设置 proxyIP/PROXYIP !!!\n` + 订阅器 += `\nSUB(优选订阅生成器): ${sub}`; + } + + return ` +################################################################ +Subscribe / sub 订阅地址, 支持 Base64、clash-meta、sing-box 订阅格式 +--------------------------------------------------------------- +快速自适应订阅地址: +https://${proxyhost}${hostName}/${password} +https://${proxyhost}${hostName}/${password}?sub + +Base64订阅地址: +https://${proxyhost}${hostName}/${password}?b64 +https://${proxyhost}${hostName}/${password}?base64 + +clash订阅地址: +https://${proxyhost}${hostName}/${password}?clash + +singbox订阅地址: +https://${proxyhost}${hostName}/${password}?sb +https://${proxyhost}${hostName}/${password}?singbox + +${surge} +--------------------------------------------------------------- +################################################################ +${FileName} 配置信息 +--------------------------------------------------------------- +HOST: ${hostName} +PASSWORD: ${password} +SHA224: ${sha224Password} +FAKEPASS: ${fakeUserID} +UA: ${UA} + +${订阅器} +SUBAPI(订阅转换后端): ${subProtocol}://${subconverter} +SUBCONFIG(订阅转换配置文件): ${subconfig} +--------------------------------------------------------------- +################################################################ +v2ray +--------------------------------------------------------------- +${v2ray} +--------------------------------------------------------------- +################################################################ +clash-meta +--------------------------------------------------------------- +${clash} +--------------------------------------------------------------- +################################################################ +${atob(`dGVsZWdyYW0g5Lqk5rWB576kIOaKgOacr+Wkp+S9rH7lnKjnur/lj5HniYwhCmh0dHBzOi8vdC5tZS9DTUxpdXNzc3MKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmdpdGh1YiDpobnnm67lnLDlnYAgU3RhciFTdGFyIVN0YXIhISEKaHR0cHM6Ly9naXRodWIuY29tL2NtbGl1L2VwZWl1cwotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw==`)} +`; + } else { + if (typeof fetch != 'function') { + return 'Error: fetch is not available in this environment.'; + } + // 如果是使用默认域名,则改成一个workers的域名,订阅器会加上代理 + if (hostName.includes(".workers.dev") || hostName.includes(".pages.dev")){ + fakeHostName = `${fakeHostName}.workers.dev`; + } else { + fakeHostName = `${fakeHostName}.xyz` + } + + let url = `https://${sub}/sub?host=${fakeHostName}&pw=${fakeUserID}&password=${fakeUserID + atob('JmVwZWl1cz1jbWxpdSZwcm94eWlwPQ==') + RproxyIP}`; + let isBase64 = true; + let newAddressesapi = []; + let newAddressescsv = []; + + if (!sub || sub == "") { + if(hostName.includes('workers.dev') || hostName.includes('pages.dev')) { + if (proxyhostsURL && (!proxyhosts || proxyhosts.length == 0)) { + try { + const response = await fetch(proxyhostsURL); + + if (!response.ok) { + console.error('获取地址时出错:', response.status, response.statusText); + return; // 如果有错误,直接返回 + } + + const text = await response.text(); + const lines = text.split('\n'); + // 过滤掉空行或只包含空白字符的行 + const nonEmptyLines = lines.filter(line => line.trim() !== ''); + + proxyhosts = proxyhosts.concat(nonEmptyLines); + } catch (error) { + console.error('获取地址时出错:', error); + } + } + // 使用Set对象去重 + proxyhosts = [...new Set(proxyhosts)]; + } + + newAddressesapi = await getAddressesapi(addressesapi); + newAddressescsv = await getAddressescsv('TRUE'); + url = `https://${hostName}/${fakeUserID}`; + } + + if (!userAgent.includes(('CF-Workers-SUB').toLowerCase())){ + if ((userAgent.includes('clash') && !userAgent.includes('nekobox')) || ( _url.searchParams.has('clash'))) { + url = `${subProtocol}://${subconverter}/sub?target=clash&url=${encodeURIComponent(url)}&insert=false&config=${encodeURIComponent(subconfig)}&emoji=true&list=false&tfo=false&scv=true&fdn=false&sort=false&new_name=true`; + isBase64 = false; + } else if (userAgent.includes('sing-box') || userAgent.includes('singbox') || _url.searchParams.has('singbox') || _url.searchParams.has('sb')) { + url = `${subProtocol}://${subconverter}/sub?target=singbox&url=${encodeURIComponent(url)}&insert=false&config=${encodeURIComponent(subconfig)}&emoji=true&list=false&tfo=false&scv=true&fdn=false&sort=false&new_name=true`; + isBase64 = false; + } else if (userAgent.includes('surge') || _url.searchParams.has('surge')) { + url = `${subProtocol}://${subconverter}/sub?target=surge&ver=4&url=${encodeURIComponent(url)}&insert=false&config=${encodeURIComponent(subconfig)}&emoji=true&list=false&xudp=false&udp=false&tfo=false&expand=true&scv=true&fdn=false`; + isBase64 = false; + } + } + + try { + let content; + if ((!sub || sub == "") && isBase64 == true) { + content = await subAddresses(fakeHostName,fakeUserID,userAgent,newAddressesapi,newAddressescsv); + } else { + const response = await fetch(url ,{ + headers: { + 'User-Agent': atob('Q0YtV29ya2Vycy1lcGVpdXMvY21saXU='), + }}); + content = await response.text(); + } + + if (_url.pathname == `/${fakeUserID}`) return content; + + content = revertFakeInfo(content, password, hostName, isBase64); + if (userAgent.includes('surge') || _url.searchParams.has('surge')) content = surge(content, `https://${hostName}/${password}?surge`); + return content; + } catch (error) { + console.error('Error fetching content:', error); + return `Error fetching content: ${error.message}`; + } + } +} + +async function sendMessage(type, ip, add_data = "") { + if ( BotToken !== '' && ChatID !== ''){ + let msg = ""; + const response = await fetch(`http://ip-api.com/json/${ip}?lang=zh-CN`); + if (response.status == 200) { + const ipInfo = await response.json(); + msg = `${type}\nIP: ${ip}\n国家: ${ipInfo.country}\n城市: ${ipInfo.city}\n组织: ${ipInfo.org}\nASN: ${ipInfo.as}\n${add_data}`; + } else { + msg = `${type}\nIP: ${ip}\n${add_data}`; + } + + let url = "https://api.telegram.org/bot"+ BotToken +"/sendMessage?chat_id=" + ChatID + "&parse_mode=HTML&text=" + encodeURIComponent(msg); + return fetch(url, { + method: 'get', + headers: { + 'Accept': 'text/html,application/xhtml+xml,application/xml;', + 'Accept-Encoding': 'gzip, deflate, br', + 'User-Agent': 'Mozilla/5.0 Chrome/90.0.4430.72' + } + }); + } +} +let proxyIPPool = []; +function subAddresses(host,pw,userAgent,newAddressesapi,newAddressescsv) { + addresses = addresses.concat(newAddressesapi); + addresses = addresses.concat(newAddressescsv); + // 使用Set对象去重 + const uniqueAddresses = [...new Set(addresses)]; + + const responseBody = uniqueAddresses.map(address => { + let port = "-1"; + let addressid = address; + + const match = addressid.match(regex); + if (!match) { + if (address.includes(':') && address.includes('#')) { + const parts = address.split(':'); + address = parts[0]; + const subParts = parts[1].split('#'); + port = subParts[0]; + addressid = subParts[1]; + } else if (address.includes(':')) { + const parts = address.split(':'); + address = parts[0]; + port = parts[1]; + } else if (address.includes('#')) { + const parts = address.split('#'); + address = parts[0]; + addressid = parts[1]; + } + + if (addressid.includes(':')) { + addressid = addressid.split(':')[0]; + } + } else { + address = match[1]; + port = match[2] || port; + addressid = match[3] || address; + } + + const httpsPorts = ["2053","2083","2087","2096","8443"]; + if (!isValidIPv4(address) && port == "-1") { + for (let httpsPort of httpsPorts) { + if (address.includes(httpsPort)) { + port = httpsPort; + break; + } + } + } + if (port == "-1") port = "443"; + + let 伪装域名 = host ; + let 最终路径 = '/?ed=2560' ; + let 节点备注 = ''; + + if(proxyhosts.length > 0 && (伪装域名.includes('.workers.dev') || 伪装域名.includes('pages.dev'))) { + 最终路径 = `/${伪装域名}${最终路径}`; + 伪装域名 = proxyhosts[Math.floor(Math.random() * proxyhosts.length)]; + 节点备注 = ` 已启用临时域名中转服务,请尽快绑定自定义域!`; + } + const matchingProxyIP = proxyIPPool.find(proxyIP => proxyIP.includes(address)); + if (matchingProxyIP) 最终路径 += `&proxyip=${matchingProxyIP}`; + let 密码 = pw; + if (!userAgent.includes('subconverter')) 密码 = encodeURIComponent(pw); + + const 啥啥啥_写的这是啥啊 = 'dHJvamFu'; + const 协议类型 = atob(啥啥啥_写的这是啥啊); + const trojanLink = `${协议类型}://${密码}@${address}:${port}?security=tls&sni=${伪装域名}&fp=randomized&type=ws&host=${伪装域名}&path=${encodeURIComponent(最终路径)}#${encodeURIComponent(addressid + 节点备注)}`; + + return trojanLink; + }).join('\n'); + + const base64Response = btoa(responseBody); // 重新进行 Base64 编码 + + return base64Response; +} + +async function getAddressesapi(api) { + if (!api || api.length === 0) return []; + + let newapi = ""; + + // 创建一个AbortController对象,用于控制fetch请求的取消 + const controller = new AbortController(); + + const timeout = setTimeout(() => { + controller.abort(); // 取消所有请求 + }, 2000); // 2秒后触发 + + try { + // 使用Promise.allSettled等待所有API请求完成,无论成功或失败 + // 对api数组进行遍历,对每个API地址发起fetch请求 + const responses = await Promise.allSettled(api.map(apiUrl => fetch(apiUrl, { + method: 'get', + headers: { + 'Accept': 'text/html,application/xhtml+xml,application/xml;', + 'User-Agent': atob('Q0YtV29ya2Vycy1lcGVpdXMvY21saXU=') + }, + signal: controller.signal // 将AbortController的信号量添加到fetch请求中,以便于需要时可以取消请求 + }).then(response => response.ok ? response.text() : Promise.reject()))); + + // 遍历所有响应 + for (const [index, response] of responses.entries()) { + // 检查响应状态是否为'fulfilled',即请求成功完成 + if (response.status === 'fulfilled') { + // 获取响应的内容 + const content = await response.value; + + // 验证当前apiUrl是否带有'proxyip=true' + if (api[index].includes('proxyip=true')) { + // 如果URL带有'proxyip=true',则将内容添加到proxyIPPool + proxyIPPool = proxyIPPool.concat((await ADD(content)).map(item => { + const baseItem = item.split('#')[0] || item; + if (baseItem.includes(':')) { + const port = baseItem.split(':')[1]; + if (!httpsPorts.includes(port)) { + return baseItem; + } + } else { + return `${baseItem}:443`; + } + return null; // 不符合条件时返回 null + }).filter(Boolean)); // 过滤掉 null 值 + } + // 将内容添加到newapi中 + newapi += content + '\n'; + } + } + } catch (error) { + console.error(error); + } finally { + // 无论成功或失败,最后都清除设置的超时定时器 + clearTimeout(timeout); + } + + const newAddressesapi = await ADD(newapi); + + // 返回处理后的结果 + return newAddressesapi; +} + +async function getAddressescsv(tls) { + if (!addressescsv || addressescsv.length === 0) { + return []; + } + + let newAddressescsv = []; + + for (const csvUrl of addressescsv) { + try { + const response = await fetch(csvUrl); + + if (!response.ok) { + console.error('获取CSV地址时出错:', response.status, response.statusText); + continue; + } + + const text = await response.text();// 使用正确的字符编码解析文本内容 + let lines; + if (text.includes('\r\n')){ + lines = text.split('\r\n'); + } else { + lines = text.split('\n'); + } + + // 检查CSV头部是否包含必需字段 + const header = lines[0].split(','); + const tlsIndex = header.indexOf('TLS'); + + const ipAddressIndex = 0;// IP地址在 CSV 头部的位置 + const portIndex = 1;// 端口在 CSV 头部的位置 + const dataCenterIndex = tlsIndex + 1; // 数据中心是 TLS 的后一个字段 + + if (tlsIndex === -1) { + console.error('CSV文件缺少必需的字段'); + continue; + } + + // 从第二行开始遍历CSV行 + for (let i = 1; i < lines.length; i++) { + const columns = lines[i].split(','); + const speedIndex = columns.length - 1; // 最后一个字段 + // 检查TLS是否为"TRUE"且速度大于DLS + if (columns[tlsIndex].toUpperCase() === tls && parseFloat(columns[speedIndex]) > DLS) { + const ipAddress = columns[ipAddressIndex]; + const port = columns[portIndex]; + const dataCenter = columns[dataCenterIndex]; + + const formattedAddress = `${ipAddress}:${port}#${dataCenter}`; + newAddressescsv.push(formattedAddress); + if (csvUrl.includes('proxyip=true') && columns[tlsIndex].toUpperCase() == 'true' && !httpsPorts.includes(port)) { + // 如果URL带有'proxyip=true',则将内容添加到proxyIPPool + proxyIPPool.push(`${ipAddress}:${port}`); + } + } + } + } catch (error) { + console.error('获取CSV地址时出错:', error); + continue; + } + } + + return newAddressescsv; +} + +function surge(content, url) { + let 每行内容; + if (content.includes('\r\n')){ + 每行内容 = content.split('\r\n'); + } else { + 每行内容 = content.split('\n'); + } + + let 输出内容 = ""; + for (let x of 每行内容) { + if (x.includes('= trojan,')) { + const host = x.split("sni=")[1].split(",")[0]; + const 备改内容 = `skip-cert-verify=true, tfo=false, udp-relay=false`; + const 正确内容 = `skip-cert-verify=true, ws=true, ws-path=/?ed=2560, ws-headers=Host:"${host}", tfo=false, udp-relay=false`; + 输出内容 += x.replace(new RegExp(备改内容, 'g'), 正确内容).replace("[", "").replace("]", "") + '\n'; + } else { + 输出内容 += x + '\n'; + } + } + + 输出内容 = `#!MANAGED-CONFIG ${url} interval=86400 strict=false` + 输出内容.substring(输出内容.indexOf('\n')); + return 输出内容; +} + +/** + * [js-sha256]{@link https://github.com/emn178/js-sha256} + * + * @version 0.11.0 + * @author Chen, Yi-Cyuan [emn178@gmail.com] + * @copyright Chen, Yi-Cyuan 2014-2024 + * @license MIT + */ +/*jslint bitwise: true */ +(function () { + 'use strict'; + + var ERROR = 'input is invalid type'; + var WINDOW = typeof window === 'object'; + var root = WINDOW ? window : {}; + if (root.JS_SHA256_NO_WINDOW) { + WINDOW = false; + } + var WEB_WORKER = !WINDOW && typeof self === 'object'; + var NODE_JS = !root.JS_SHA256_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node; + if (NODE_JS) { + root = global; + } else if (WEB_WORKER) { + root = self; + } + var COMMON_JS = !root.JS_SHA256_NO_COMMON_JS && typeof module === 'object' && module.exports; + var AMD = typeof define === 'function' && define.amd; + var ARRAY_BUFFER = !root.JS_SHA256_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined'; + var HEX_CHARS = '0123456789abcdef'.split(''); + var EXTRA = [-2147483648, 8388608, 32768, 128]; + var SHIFT = [24, 16, 8, 0]; + var K = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + ]; + var OUTPUT_TYPES = ['hex', 'array', 'digest', 'arrayBuffer']; + + var blocks = []; + + if (root.JS_SHA256_NO_NODE_JS || !Array.isArray) { + Array.isArray = function (obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; + }; + } + + if (ARRAY_BUFFER && (root.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) { + ArrayBuffer.isView = function (obj) { + return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer; + }; + } + + var createOutputMethod = function (outputType, is224) { + return function (message) { + return new Sha256(is224, true).update(message)[outputType](); + }; + }; + + var createMethod = function (is224) { + var method = createOutputMethod('hex', is224); + if (NODE_JS) { + method = nodeWrap(method, is224); + } + method.create = function () { + return new Sha256(is224); + }; + method.update = function (message) { + return method.create().update(message); + }; + for (var i = 0; i < OUTPUT_TYPES.length; ++i) { + var type = OUTPUT_TYPES[i]; + method[type] = createOutputMethod(type, is224); + } + return method; + }; + + var nodeWrap = function (method, is224) { + var crypto = require('crypto') + var Buffer = require('buffer').Buffer; + var algorithm = is224 ? 'sha224' : 'sha256'; + var bufferFrom; + if (Buffer.from && !root.JS_SHA256_NO_BUFFER_FROM) { + bufferFrom = Buffer.from; + } else { + bufferFrom = function (message) { + return new Buffer(message); + }; + } + var nodeMethod = function (message) { + if (typeof message === 'string') { + return crypto.createHash(algorithm).update(message, 'utf8').digest('hex'); + } else { + if (message === null || message === undefined) { + throw new Error(ERROR); + } else if (message.constructor === ArrayBuffer) { + message = new Uint8Array(message); + } + } + if (Array.isArray(message) || ArrayBuffer.isView(message) || + message.constructor === Buffer) { + return crypto.createHash(algorithm).update(bufferFrom(message)).digest('hex'); + } else { + return method(message); + } + }; + return nodeMethod; + }; + + var createHmacOutputMethod = function (outputType, is224) { + return function (key, message) { + return new HmacSha256(key, is224, true).update(message)[outputType](); + }; + }; + + var createHmacMethod = function (is224) { + var method = createHmacOutputMethod('hex', is224); + method.create = function (key) { + return new HmacSha256(key, is224); + }; + method.update = function (key, message) { + return method.create(key).update(message); + }; + for (var i = 0; i < OUTPUT_TYPES.length; ++i) { + var type = OUTPUT_TYPES[i]; + method[type] = createHmacOutputMethod(type, is224); + } + return method; + }; + + function Sha256(is224, sharedMemory) { + if (sharedMemory) { + blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = + blocks[4] = blocks[5] = blocks[6] = blocks[7] = + blocks[8] = blocks[9] = blocks[10] = blocks[11] = + blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + this.blocks = blocks; + } else { + this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + } + + if (is224) { + this.h0 = 0xc1059ed8; + this.h1 = 0x367cd507; + this.h2 = 0x3070dd17; + this.h3 = 0xf70e5939; + this.h4 = 0xffc00b31; + this.h5 = 0x68581511; + this.h6 = 0x64f98fa7; + this.h7 = 0xbefa4fa4; + } else { // 256 + this.h0 = 0x6a09e667; + this.h1 = 0xbb67ae85; + this.h2 = 0x3c6ef372; + this.h3 = 0xa54ff53a; + this.h4 = 0x510e527f; + this.h5 = 0x9b05688c; + this.h6 = 0x1f83d9ab; + this.h7 = 0x5be0cd19; + } + + this.block = this.start = this.bytes = this.hBytes = 0; + this.finalized = this.hashed = false; + this.first = true; + this.is224 = is224; + } + + Sha256.prototype.update = function (message) { + if (this.finalized) { + return; + } + var notString, type = typeof message; + if (type !== 'string') { + if (type === 'object') { + if (message === null) { + throw new Error(ERROR); + } else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) { + message = new Uint8Array(message); + } else if (!Array.isArray(message)) { + if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) { + throw new Error(ERROR); + } + } + } else { + throw new Error(ERROR); + } + notString = true; + } + var code, index = 0, i, length = message.length, blocks = this.blocks; + while (index < length) { + if (this.hashed) { + this.hashed = false; + blocks[0] = this.block; + this.block = blocks[16] = blocks[1] = blocks[2] = blocks[3] = + blocks[4] = blocks[5] = blocks[6] = blocks[7] = + blocks[8] = blocks[9] = blocks[10] = blocks[11] = + blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + } + + if (notString) { + for (i = this.start; index < length && i < 64; ++index) { + blocks[i >>> 2] |= message[index] << SHIFT[i++ & 3]; + } + } else { + for (i = this.start; index < length && i < 64; ++index) { + code = message.charCodeAt(index); + if (code < 0x80) { + blocks[i >>> 2] |= code << SHIFT[i++ & 3]; + } else if (code < 0x800) { + blocks[i >>> 2] |= (0xc0 | (code >>> 6)) << SHIFT[i++ & 3]; + blocks[i >>> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } else if (code < 0xd800 || code >= 0xe000) { + blocks[i >>> 2] |= (0xe0 | (code >>> 12)) << SHIFT[i++ & 3]; + blocks[i >>> 2] |= (0x80 | ((code >>> 6) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >>> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } else { + code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff)); + blocks[i >>> 2] |= (0xf0 | (code >>> 18)) << SHIFT[i++ & 3]; + blocks[i >>> 2] |= (0x80 | ((code >>> 12) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >>> 2] |= (0x80 | ((code >>> 6) & 0x3f)) << SHIFT[i++ & 3]; + blocks[i >>> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3]; + } + } + } + + this.lastByteIndex = i; + this.bytes += i - this.start; + if (i >= 64) { + this.block = blocks[16]; + this.start = i - 64; + this.hash(); + this.hashed = true; + } else { + this.start = i; + } + } + if (this.bytes > 4294967295) { + this.hBytes += this.bytes / 4294967296 << 0; + this.bytes = this.bytes % 4294967296; + } + return this; + }; + + Sha256.prototype.finalize = function () { + if (this.finalized) { + return; + } + this.finalized = true; + var blocks = this.blocks, i = this.lastByteIndex; + blocks[16] = this.block; + blocks[i >>> 2] |= EXTRA[i & 3]; + this.block = blocks[16]; + if (i >= 56) { + if (!this.hashed) { + this.hash(); + } + blocks[0] = this.block; + blocks[16] = blocks[1] = blocks[2] = blocks[3] = + blocks[4] = blocks[5] = blocks[6] = blocks[7] = + blocks[8] = blocks[9] = blocks[10] = blocks[11] = + blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0; + } + blocks[14] = this.hBytes << 3 | this.bytes >>> 29; + blocks[15] = this.bytes << 3; + this.hash(); + }; + + Sha256.prototype.hash = function () { + var a = this.h0, b = this.h1, c = this.h2, d = this.h3, e = this.h4, f = this.h5, g = this.h6, + h = this.h7, blocks = this.blocks, j, s0, s1, maj, t1, t2, ch, ab, da, cd, bc; + + for (j = 16; j < 64; ++j) { + // rightrotate + t1 = blocks[j - 15]; + s0 = ((t1 >>> 7) | (t1 << 25)) ^ ((t1 >>> 18) | (t1 << 14)) ^ (t1 >>> 3); + t1 = blocks[j - 2]; + s1 = ((t1 >>> 17) | (t1 << 15)) ^ ((t1 >>> 19) | (t1 << 13)) ^ (t1 >>> 10); + blocks[j] = blocks[j - 16] + s0 + blocks[j - 7] + s1 << 0; + } + + bc = b & c; + for (j = 0; j < 64; j += 4) { + if (this.first) { + if (this.is224) { + ab = 300032; + t1 = blocks[0] - 1413257819; + h = t1 - 150054599 << 0; + d = t1 + 24177077 << 0; + } else { + ab = 704751109; + t1 = blocks[0] - 210244248; + h = t1 - 1521486534 << 0; + d = t1 + 143694565 << 0; + } + this.first = false; + } else { + s0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10)); + s1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7)); + ab = a & b; + maj = ab ^ (a & c) ^ bc; + ch = (e & f) ^ (~e & g); + t1 = h + s1 + ch + K[j] + blocks[j]; + t2 = s0 + maj; + h = d + t1 << 0; + d = t1 + t2 << 0; + } + s0 = ((d >>> 2) | (d << 30)) ^ ((d >>> 13) | (d << 19)) ^ ((d >>> 22) | (d << 10)); + s1 = ((h >>> 6) | (h << 26)) ^ ((h >>> 11) | (h << 21)) ^ ((h >>> 25) | (h << 7)); + da = d & a; + maj = da ^ (d & b) ^ ab; + ch = (h & e) ^ (~h & f); + t1 = g + s1 + ch + K[j + 1] + blocks[j + 1]; + t2 = s0 + maj; + g = c + t1 << 0; + c = t1 + t2 << 0; + s0 = ((c >>> 2) | (c << 30)) ^ ((c >>> 13) | (c << 19)) ^ ((c >>> 22) | (c << 10)); + s1 = ((g >>> 6) | (g << 26)) ^ ((g >>> 11) | (g << 21)) ^ ((g >>> 25) | (g << 7)); + cd = c & d; + maj = cd ^ (c & a) ^ da; + ch = (g & h) ^ (~g & e); + t1 = f + s1 + ch + K[j + 2] + blocks[j + 2]; + t2 = s0 + maj; + f = b + t1 << 0; + b = t1 + t2 << 0; + s0 = ((b >>> 2) | (b << 30)) ^ ((b >>> 13) | (b << 19)) ^ ((b >>> 22) | (b << 10)); + s1 = ((f >>> 6) | (f << 26)) ^ ((f >>> 11) | (f << 21)) ^ ((f >>> 25) | (f << 7)); + bc = b & c; + maj = bc ^ (b & d) ^ cd; + ch = (f & g) ^ (~f & h); + t1 = e + s1 + ch + K[j + 3] + blocks[j + 3]; + t2 = s0 + maj; + e = a + t1 << 0; + a = t1 + t2 << 0; + this.chromeBugWorkAround = true; + } + + this.h0 = this.h0 + a << 0; + this.h1 = this.h1 + b << 0; + this.h2 = this.h2 + c << 0; + this.h3 = this.h3 + d << 0; + this.h4 = this.h4 + e << 0; + this.h5 = this.h5 + f << 0; + this.h6 = this.h6 + g << 0; + this.h7 = this.h7 + h << 0; + }; + + Sha256.prototype.hex = function () { + this.finalize(); + + var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5, + h6 = this.h6, h7 = this.h7; + + var hex = HEX_CHARS[(h0 >>> 28) & 0x0F] + HEX_CHARS[(h0 >>> 24) & 0x0F] + + HEX_CHARS[(h0 >>> 20) & 0x0F] + HEX_CHARS[(h0 >>> 16) & 0x0F] + + HEX_CHARS[(h0 >>> 12) & 0x0F] + HEX_CHARS[(h0 >>> 8) & 0x0F] + + HEX_CHARS[(h0 >>> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] + + HEX_CHARS[(h1 >>> 28) & 0x0F] + HEX_CHARS[(h1 >>> 24) & 0x0F] + + HEX_CHARS[(h1 >>> 20) & 0x0F] + HEX_CHARS[(h1 >>> 16) & 0x0F] + + HEX_CHARS[(h1 >>> 12) & 0x0F] + HEX_CHARS[(h1 >>> 8) & 0x0F] + + HEX_CHARS[(h1 >>> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] + + HEX_CHARS[(h2 >>> 28) & 0x0F] + HEX_CHARS[(h2 >>> 24) & 0x0F] + + HEX_CHARS[(h2 >>> 20) & 0x0F] + HEX_CHARS[(h2 >>> 16) & 0x0F] + + HEX_CHARS[(h2 >>> 12) & 0x0F] + HEX_CHARS[(h2 >>> 8) & 0x0F] + + HEX_CHARS[(h2 >>> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] + + HEX_CHARS[(h3 >>> 28) & 0x0F] + HEX_CHARS[(h3 >>> 24) & 0x0F] + + HEX_CHARS[(h3 >>> 20) & 0x0F] + HEX_CHARS[(h3 >>> 16) & 0x0F] + + HEX_CHARS[(h3 >>> 12) & 0x0F] + HEX_CHARS[(h3 >>> 8) & 0x0F] + + HEX_CHARS[(h3 >>> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] + + HEX_CHARS[(h4 >>> 28) & 0x0F] + HEX_CHARS[(h4 >>> 24) & 0x0F] + + HEX_CHARS[(h4 >>> 20) & 0x0F] + HEX_CHARS[(h4 >>> 16) & 0x0F] + + HEX_CHARS[(h4 >>> 12) & 0x0F] + HEX_CHARS[(h4 >>> 8) & 0x0F] + + HEX_CHARS[(h4 >>> 4) & 0x0F] + HEX_CHARS[h4 & 0x0F] + + HEX_CHARS[(h5 >>> 28) & 0x0F] + HEX_CHARS[(h5 >>> 24) & 0x0F] + + HEX_CHARS[(h5 >>> 20) & 0x0F] + HEX_CHARS[(h5 >>> 16) & 0x0F] + + HEX_CHARS[(h5 >>> 12) & 0x0F] + HEX_CHARS[(h5 >>> 8) & 0x0F] + + HEX_CHARS[(h5 >>> 4) & 0x0F] + HEX_CHARS[h5 & 0x0F] + + HEX_CHARS[(h6 >>> 28) & 0x0F] + HEX_CHARS[(h6 >>> 24) & 0x0F] + + HEX_CHARS[(h6 >>> 20) & 0x0F] + HEX_CHARS[(h6 >>> 16) & 0x0F] + + HEX_CHARS[(h6 >>> 12) & 0x0F] + HEX_CHARS[(h6 >>> 8) & 0x0F] + + HEX_CHARS[(h6 >>> 4) & 0x0F] + HEX_CHARS[h6 & 0x0F]; + if (!this.is224) { + hex += HEX_CHARS[(h7 >>> 28) & 0x0F] + HEX_CHARS[(h7 >>> 24) & 0x0F] + + HEX_CHARS[(h7 >>> 20) & 0x0F] + HEX_CHARS[(h7 >>> 16) & 0x0F] + + HEX_CHARS[(h7 >>> 12) & 0x0F] + HEX_CHARS[(h7 >>> 8) & 0x0F] + + HEX_CHARS[(h7 >>> 4) & 0x0F] + HEX_CHARS[h7 & 0x0F]; + } + return hex; + }; + + Sha256.prototype.toString = Sha256.prototype.hex; + + Sha256.prototype.digest = function () { + this.finalize(); + + var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3, h4 = this.h4, h5 = this.h5, + h6 = this.h6, h7 = this.h7; + + var arr = [ + (h0 >>> 24) & 0xFF, (h0 >>> 16) & 0xFF, (h0 >>> 8) & 0xFF, h0 & 0xFF, + (h1 >>> 24) & 0xFF, (h1 >>> 16) & 0xFF, (h1 >>> 8) & 0xFF, h1 & 0xFF, + (h2 >>> 24) & 0xFF, (h2 >>> 16) & 0xFF, (h2 >>> 8) & 0xFF, h2 & 0xFF, + (h3 >>> 24) & 0xFF, (h3 >>> 16) & 0xFF, (h3 >>> 8) & 0xFF, h3 & 0xFF, + (h4 >>> 24) & 0xFF, (h4 >>> 16) & 0xFF, (h4 >>> 8) & 0xFF, h4 & 0xFF, + (h5 >>> 24) & 0xFF, (h5 >>> 16) & 0xFF, (h5 >>> 8) & 0xFF, h5 & 0xFF, + (h6 >>> 24) & 0xFF, (h6 >>> 16) & 0xFF, (h6 >>> 8) & 0xFF, h6 & 0xFF + ]; + if (!this.is224) { + arr.push((h7 >>> 24) & 0xFF, (h7 >>> 16) & 0xFF, (h7 >>> 8) & 0xFF, h7 & 0xFF); + } + return arr; + }; + + Sha256.prototype.array = Sha256.prototype.digest; + + Sha256.prototype.arrayBuffer = function () { + this.finalize(); + + var buffer = new ArrayBuffer(this.is224 ? 28 : 32); + var dataView = new DataView(buffer); + dataView.setUint32(0, this.h0); + dataView.setUint32(4, this.h1); + dataView.setUint32(8, this.h2); + dataView.setUint32(12, this.h3); + dataView.setUint32(16, this.h4); + dataView.setUint32(20, this.h5); + dataView.setUint32(24, this.h6); + if (!this.is224) { + dataView.setUint32(28, this.h7); + } + return buffer; + }; + + function HmacSha256(key, is224, sharedMemory) { + var i, type = typeof key; + if (type === 'string') { + var bytes = [], length = key.length, index = 0, code; + for (i = 0; i < length; ++i) { + code = key.charCodeAt(i); + if (code < 0x80) { + bytes[index++] = code; + } else if (code < 0x800) { + bytes[index++] = (0xc0 | (code >>> 6)); + bytes[index++] = (0x80 | (code & 0x3f)); + } else if (code < 0xd800 || code >= 0xe000) { + bytes[index++] = (0xe0 | (code >>> 12)); + bytes[index++] = (0x80 | ((code >>> 6) & 0x3f)); + bytes[index++] = (0x80 | (code & 0x3f)); + } else { + code = 0x10000 + (((code & 0x3ff) << 10) | (key.charCodeAt(++i) & 0x3ff)); + bytes[index++] = (0xf0 | (code >>> 18)); + bytes[index++] = (0x80 | ((code >>> 12) & 0x3f)); + bytes[index++] = (0x80 | ((code >>> 6) & 0x3f)); + bytes[index++] = (0x80 | (code & 0x3f)); + } + } + key = bytes; + } else { + if (type === 'object') { + if (key === null) { + throw new Error(ERROR); + } else if (ARRAY_BUFFER && key.constructor === ArrayBuffer) { + key = new Uint8Array(key); + } else if (!Array.isArray(key)) { + if (!ARRAY_BUFFER || !ArrayBuffer.isView(key)) { + throw new Error(ERROR); + } + } + } else { + throw new Error(ERROR); + } + } + + if (key.length > 64) { + key = (new Sha256(is224, true)).update(key).array(); + } + + var oKeyPad = [], iKeyPad = []; + for (i = 0; i < 64; ++i) { + var b = key[i] || 0; + oKeyPad[i] = 0x5c ^ b; + iKeyPad[i] = 0x36 ^ b; + } + + Sha256.call(this, is224, sharedMemory); + + this.update(iKeyPad); + this.oKeyPad = oKeyPad; + this.inner = true; + this.sharedMemory = sharedMemory; + } + HmacSha256.prototype = new Sha256(); + + HmacSha256.prototype.finalize = function () { + Sha256.prototype.finalize.call(this); + if (this.inner) { + this.inner = false; + var innerHash = this.array(); + Sha256.call(this, this.is224, this.sharedMemory); + this.update(this.oKeyPad); + this.update(innerHash); + Sha256.prototype.finalize.call(this); + } + }; + + var exports = createMethod(); + exports.sha256 = exports; + exports.sha224 = createMethod(true); + exports.sha256.hmac = createHmacMethod(); + exports.sha224.hmac = createHmacMethod(true); + + if (COMMON_JS) { + module.exports = exports; + } else { + root.sha256 = exports.sha256; + root.sha224 = exports.sha224; + if (AMD) { + define(function () { + return exports; + }); + } + } +})(); + +/** + * + * @param {number} addressType + * @param {string} addressRemote + * @param {number} portRemote + * @param {function} log The logging function. + */ +async function socks5Connect(addressType, addressRemote, portRemote, log) { + const { username, password, hostname, port } = parsedSocks5Address; + // Connect to the SOCKS server + const socket = connect({ + hostname, + port, + }); + + // Request head format (Worker -> Socks Server): + // +----+----------+----------+ + // |VER | NMETHODS | METHODS | + // +----+----------+----------+ + // | 1 | 1 | 1 to 255 | + // +----+----------+----------+ + + // https://en.wikipedia.org/wiki/SOCKS#SOCKS5 + // For METHODS: + // 0x00 NO AUTHENTICATION REQUIRED + // 0x02 USERNAME/PASSWORD https://datatracker.ietf.org/doc/html/rfc1929 + const socksGreeting = new Uint8Array([5, 2, 0, 2]); + + const writer = socket.writable.getWriter(); + + await writer.write(socksGreeting); + log('sent socks greeting'); + + const reader = socket.readable.getReader(); + const encoder = new TextEncoder(); + let res = (await reader.read()).value; + // Response format (Socks Server -> Worker): + // +----+--------+ + // |VER | METHOD | + // +----+--------+ + // | 1 | 1 | + // +----+--------+ + if (res[0] !== 0x05) { + log(`socks server version error: ${res[0]} expected: 5`); + return; + } + if (res[1] === 0xff) { + log("no acceptable methods"); + return; + } + + // if return 0x0502 + if (res[1] === 0x02) { + log("socks server needs auth"); + if (!username || !password) { + log("please provide username/password"); + return; + } + // +----+------+----------+------+----------+ + // |VER | ULEN | UNAME | PLEN | PASSWD | + // +----+------+----------+------+----------+ + // | 1 | 1 | 1 to 255 | 1 | 1 to 255 | + // +----+------+----------+------+----------+ + const authRequest = new Uint8Array([ + 1, + username.length, + ...encoder.encode(username), + password.length, + ...encoder.encode(password) + ]); + await writer.write(authRequest); + res = (await reader.read()).value; + // expected 0x0100 + if (res[0] !== 0x01 || res[1] !== 0x00) { + log("fail to auth socks server"); + return; + } + } + + // Request data format (Worker -> Socks Server): + // +----+-----+-------+------+----------+----------+ + // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | + // +----+-----+-------+------+----------+----------+ + // | 1 | 1 | X'00' | 1 | Variable | 2 | + // +----+-----+-------+------+----------+----------+ + // ATYP: address type of following address + // 0x01: IPv4 address + // 0x03: Domain name + // 0x04: IPv6 address + // DST.ADDR: desired destination address + // DST.PORT: desired destination port in network octet order + + // addressType + // 0x01: IPv4 address + // 0x03: Domain name + // 0x04: IPv6 address + // 1--> ipv4 addressLength =4 + // 2--> domain name + // 3--> ipv6 addressLength =16 + let DSTADDR; // DSTADDR = ATYP + DST.ADDR + switch (addressType) { + case 1: + DSTADDR = new Uint8Array( + [1, ...addressRemote.split('.').map(Number)] + ); + break; + case 3: + DSTADDR = new Uint8Array( + [3, addressRemote.length, ...encoder.encode(addressRemote)] + ); + break; + case 4: + DSTADDR = new Uint8Array( + [4, ...addressRemote.split(':').flatMap(x => [parseInt(x.slice(0, 2), 16), parseInt(x.slice(2), 16)])] + ); + break; + default: + log(`invild addressType is ${addressType}`); + return; + } + const socksRequest = new Uint8Array([5, 1, 0, ...DSTADDR, portRemote >> 8, portRemote & 0xff]); + await writer.write(socksRequest); + log('sent socks request'); + + res = (await reader.read()).value; + // Response format (Socks Server -> Worker): + // +----+-----+-------+------+----------+----------+ + // |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | + // +----+-----+-------+------+----------+----------+ + // | 1 | 1 | X'00' | 1 | Variable | 2 | + // +----+-----+-------+------+----------+----------+ + if (res[1] === 0x00) { + log("socks connection opened"); + } else { + log("fail to open socks connection"); + return; + } + writer.releaseLock(); + reader.releaseLock(); + return socket; +} + + +/** + * + * @param {string} address + */ +function socks5AddressParser(address) { + let [latter, former] = address.split("@").reverse(); + let username, password, hostname, port; + if (former) { + const formers = former.split(":"); + if (formers.length !== 2) { + throw new Error('Invalid SOCKS address format'); + } + [username, password] = formers; + } + const latters = latter.split(":"); + port = Number(latters.pop()); + if (isNaN(port)) { + throw new Error('Invalid SOCKS address format'); + } + hostname = latters.join(":"); + const regex = /^\[.*\]$/; + if (hostname.includes(":") && !regex.test(hostname)) { + throw new Error('Invalid SOCKS address format'); + } + //if (/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(hostname)) hostname = `${atob('d3d3Lg==')}${hostname}${atob('LmlwLjA5MDIyNy54eXo=')}`; + return { + username, + password, + hostname, + port, + } +} + +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); +} \ No newline at end of file