From 211492f1612dfc752ca0d1e5fdcac4fc996577d4 Mon Sep 17 00:00:00 2001 From: cmliu Date: Tue, 27 May 2025 00:55:49 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 明文源码.js | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 210 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 6dbbf1f..7442133 100644 --- a/README.md +++ b/README.md @@ -273,4 +273,4 @@ - [SHIJS1999/cloudflare-worker-vless-ip](https://github.com/SHIJS1999/cloudflare-worker-vless-ip) - [Stanley-baby](https://github.com/Stanley-baby) - [ACL4SSR](https://github.com/ACL4SSR/ACL4SSR/tree/master/Clash/config) -- [股神](https://t.me/CF_NAT/38885) \ No newline at end of file +- [股神](https://t.me/CF_NAT/38889) \ No newline at end of file diff --git a/明文源码.js b/明文源码.js index 1ba482b..a65fc18 100644 --- a/明文源码.js +++ b/明文源码.js @@ -376,12 +376,12 @@ async function handleTCPOutBound(remoteSocket, addressType, addressRemote, portR } async function connectAndWrite(address, port, socks = false, http = false) { - log(`connected to ${address}:${port}`); + 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 - ? (http ? await httpConnect(address, port, log) : await socks5Connect(addressType, address, port, log)) - : connect({ hostname: address, port: port }); + // 先确定连接方式,再创建连接 + const tcpSocket = socks + ? (http ? await httpConnect(address, port, log) : await socks5Connect(addressType, address, port, log)) + : connect({ hostname: address, port: port }); remoteSocket.value = tcpSocket; //log(`connected to ${address}:${port}`); @@ -392,6 +392,18 @@ async function handleTCPOutBound(remoteSocket, addressType, addressRemote, portR return tcpSocket; } + async function nat64() { + const nat64Proxyip = `[${await resolveToIPv6(addressRemote)}]`; + log(`NAT64 代理连接到 ${nat64Proxyip}:443`); + tcpSocket = await connectAndWrite(nat64Proxyip, '443'); + tcpSocket.closed.catch(error => { + console.log('retry tcpSocket closed error', error); + }).finally(() => { + safeCloseWebSocket(webSocket); + }) + remoteSocketToWS(tcpSocket, webSocket, 维列斯ResponseHeader, null, log); + } + /** * 重试函数:当 Cloudflare 的 TCP Socket 没有传入数据时,我们尝试重定向 IP * 这可能是因为某些网络问题导致的连接失败 @@ -414,14 +426,15 @@ async function handleTCPOutBound(remoteSocket, addressType, addressRemote, portR if (proxyIP.includes('.tp')) portRemote = proxyIP.split('.tp')[1].split('.')[0] || portRemote; tcpSocket = await connectAndWrite(proxyIP.toLowerCase() || addressRemote, portRemote); } - // 无论重试是否成功,都要关闭 WebSocket(可能是为了重新建立连接) + /* 无论重试是否成功,都要关闭 WebSocket(可能是为了重新建立连接) tcpSocket.closed.catch(error => { console.log('retry tcpSocket closed error', error); }).finally(() => { safeCloseWebSocket(webSocket); }) + */ // 建立从远程 Socket 到 WebSocket 的数据流 - remoteSocketToWS(tcpSocket, webSocket, 维列斯ResponseHeader, null, log); + remoteSocketToWS(tcpSocket, webSocket, 维列斯ResponseHeader, nat64, log); } let useSocks = false; @@ -2339,4 +2352,192 @@ async function KV(request, env, txt = 'ADD.txt') { headers: { "Content-Type": "text/plain;charset=utf-8" } }); } -} \ No newline at end of file +} + +async function resolveToIPv6(target) { + // 检查是否为IPv4 + function isIPv4(str) { + const parts = str.split('.'); + return parts.length === 4 && parts.every(part => { + const num = parseInt(part, 10); + return num >= 0 && num <= 255 && part === num.toString(); + }); + } + + // 检查是否为IPv6 + function isIPv6(str) { + return str.includes(':') && /^[0-9a-fA-F:]+$/.test(str); + } + + // 获取域名的IPv4地址 + async function fetchIPv4(domain) { + const url = `https://cloudflare-dns.com/dns-query?name=${domain}&type=A`; + const response = await fetch(url, { + headers: { 'Accept': 'application/dns-json' } + }); + + if (!response.ok) throw new Error('DNS查询失败'); + + const data = await response.json(); + const ipv4s = (data.Answer || []) + .filter(record => record.type === 1) + .map(record => record.data); + + if (ipv4s.length === 0) throw new Error('未找到IPv4地址'); + return ipv4s[Math.floor(Math.random() * ipv4s.length)]; + } + + // 查询NAT64 IPv6地址 + async function queryNAT64(domain) { + const socket = connect(atob('ZG90Lm5hdDY0LmRrOjg1Mw=='), { + secureTransport: 'on', + allowHalfOpen: false + }); + + const writer = socket.writable.getWriter(); + const reader = socket.readable.getReader(); + + try { + // 发送DNS查询 + const query = buildDNSQuery(domain); + const queryWithLength = new Uint8Array(query.length + 2); + queryWithLength[0] = query.length >> 8; + queryWithLength[1] = query.length & 0xFF; + queryWithLength.set(query, 2); + await writer.write(queryWithLength); + + // 读取响应 + const response = await readDNSResponse(reader); + const ipv6s = parseIPv6(response); + + return ipv6s.length > 0 ? ipv6s[0] : '未找到IPv6地址'; + } finally { + await writer.close(); + await reader.cancel(); + } + } + + // 构建DNS查询包 + function buildDNSQuery(domain) { + const buffer = new ArrayBuffer(512); + const view = new DataView(buffer); + let offset = 0; + + // DNS头部 + view.setUint16(offset, Math.floor(Math.random() * 65536)); offset += 2; // ID + view.setUint16(offset, 0x0100); offset += 2; // 标志 + view.setUint16(offset, 1); offset += 2; // 问题数 + view.setUint16(offset, 0); offset += 6; // 答案数/权威数/附加数 + + // 域名编码 + for (const label of domain.split('.')) { + view.setUint8(offset++, label.length); + for (let i = 0; i < label.length; i++) { + view.setUint8(offset++, label.charCodeAt(i)); + } + } + view.setUint8(offset++, 0); // 结束标记 + + // 查询类型和类 + view.setUint16(offset, 28); offset += 2; // AAAA记录 + view.setUint16(offset, 1); offset += 2; // IN类 + + return new Uint8Array(buffer, 0, offset); + } + + // 读取DNS响应 + async function readDNSResponse(reader) { + const chunks = []; + let totalLength = 0; + let expectedLength = null; + + while (true) { + const { value, done } = await reader.read(); + if (done) break; + + chunks.push(value); + totalLength += value.length; + + if (expectedLength === null && totalLength >= 2) { + expectedLength = (chunks[0][0] << 8) | chunks[0][1]; + } + + if (expectedLength !== null && totalLength >= expectedLength + 2) { + break; + } + } + + // 合并数据并跳过长度前缀 + const fullResponse = new Uint8Array(totalLength); + let offset = 0; + for (const chunk of chunks) { + fullResponse.set(chunk, offset); + offset += chunk.length; + } + + return fullResponse.slice(2); + } + + // 解析IPv6地址 + function parseIPv6(response) { + const view = new DataView(response.buffer); + let offset = 12; // 跳过DNS头部 + + // 跳过问题部分 + while (view.getUint8(offset) !== 0) { + offset += view.getUint8(offset) + 1; + } + offset += 5; + + const answers = []; + const answerCount = view.getUint16(6); // 答案数量 + + for (let i = 0; i < answerCount; i++) { + // 跳过名称 + if ((view.getUint8(offset) & 0xC0) === 0xC0) { + offset += 2; + } else { + while (view.getUint8(offset) !== 0) { + offset += view.getUint8(offset) + 1; + } + offset++; + } + + const type = view.getUint16(offset); offset += 2; + offset += 6; // 跳过类和TTL + const dataLength = view.getUint16(offset); offset += 2; + + if (type === 28 && dataLength === 16) { // AAAA记录 + const parts = []; + for (let j = 0; j < 8; j++) { + parts.push(view.getUint16(offset + j * 2).toString(16)); + } + answers.push(parts.join(':')); + } + offset += dataLength; + } + + return answers; + } + + try { + // 判断输入类型并处理 + if (isIPv6(target)) { + return target; // IPv6直接返回 + } + + let domain; + if (isIPv4(target)) { + domain = target + atob('LmlwLjA5MDIyNy54eXo='); // IPv4转换为NAT64域名 + } else { + // 域名先解析IPv4再转NAT64 + const ipv4 = await fetchIPv4(target); + domain = ipv4 + atob('LmlwLjA5MDIyNy54eXo='); + } + + return await queryNAT64(domain); + } catch (error) { + console.error('解析错误:', error); + return `解析失败: ${error.message}`; + } +}