From 1b02f349be613be54076f6d63e0310f3a79b7c54 Mon Sep 17 00:00:00 2001 From: cmliu Date: Fri, 19 Dec 2025 03:09:49 +0800 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20=E6=9B=B4=E6=96=B0=E9=9A=8F=E6=9C=BA?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E5=87=BD=E6=95=B0=EF=BC=8C=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E5=B8=B8=E7=94=A8=E8=B7=AF=E5=BE=84=E7=9B=AE=E5=BD=95=E7=9A=84?= =?UTF-8?q?=E5=AE=8C=E6=95=B4=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _worker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_worker.js b/_worker.js index a3267bc..751d012 100644 --- a/_worker.js +++ b/_worker.js @@ -852,7 +852,7 @@ async function MD5MD5(文本) { } function 随机路径() { - const 常用路径目录 = ["#", "about", "account", "acg", "act", "activity", "ad", "admin", "ads", "ajax", "album", "albums", "anime", "api", "app", "apps", "archive", "archives", "article", "articles", "ask", "auth", "avatar", "bbs", "bd", "blog", "blogs", "book", "books", "bt", "buy", "cart", "category", "categories", "cb", "channel", "channels", "chat", "china", "city", "class", "classify", "clip", "clips", "club", "cn", "code", "collect", "collection", "comic", "comics", "community", "company", "config", "contact", "content", "course", "courses", "cp", "data", "detail", "details", "dh", "directory", "discount", "discuss", "dl", "dload", "doc", "docs", "document", "documents", "doujin", "download", "downloads", "drama", "edu", "en", "ep", "episode", "episodes", "event", "events", "f", "faq", "favorite", "favourites", "favs", "feedback", "file", "files", "film", "films", "forum", "forums", "friend", "friends", "game", "games", "gif", "go", "go.html", "go.php", "group", "groups", "help", "home", "hot", "htm", "html", "image", "images", "img", "index", "info", "intro", "item", "items", "ja", "jp", "jump", "jump.html", "jump.php", "jumping", "knowledge", "lang", "lesson", "lessons", "lib", "library", "link", "links", "list", "live", "lives", "login", "logout", "m", "mag", "magnet", "mall", "manhua", "map", "member", "members", "message", "messages", "mobile", "movie", "movies", "music", "my", "new", "news", "note", "novel", "novels", "online", "order", "out", "out.html", "out.php", "outbound", "p", "page", "pages", "pay", "payment", "pdf", "photo", "photos", "pic", "pics", "picture", "pictures", "play", "player", "playlist", "post", "posts", "product", "products", "program", "programs", "project", "qa", "question", "rank", "ranking", "read", "readme", "redirect", "redirect.html", "redirect.php", "reg", "register", "res", "resource", "retrieve", "sale", "search", "season", "seasons", "section", "seller", "series", "service", "services", "setting", "settings", "share", "shop", "show", "shows", "site", "soft", "sort", "source", "special", "star", "stars", "static", "stock", "store", "stream", "streaming", "streams", "student", "study", "tag", "tags", "task", "teacher", "team", "tech", "temp", "test", "thread", "tool", "tools", "topic", "topics", "torrent", "trade", "travel", "tv", "txt", "type", "u", "upload", "uploads", "url", "urls", "user", "users", "v", "version", "video", "videos", "view", "vip", "vod", "watch", "web", "wenku", "wiki", "work", "www", "zh", "zh-cn", "zh-tw", "zip"]; + const 常用路径目录 = ["#", "about", "account", "acg", "act", "activity", "ad", "ads", "ajax", "album", "albums", "anime", "api", "app", "apps", "archive", "archives", "article", "articles", "ask", "auth", "avatar", "bbs", "bd", "blog", "blogs", "book", "books", "bt", "buy", "cart", "category", "categories", "cb", "channel", "channels", "chat", "china", "city", "class", "classify", "clip", "clips", "club", "cn", "code", "collect", "collection", "comic", "comics", "community", "company", "config", "contact", "content", "course", "courses", "cp", "data", "detail", "details", "dh", "directory", "discount", "discuss", "dl", "dload", "doc", "docs", "document", "documents", "doujin", "download", "downloads", "drama", "edu", "en", "ep", "episode", "episodes", "event", "events", "f", "faq", "favorite", "favourites", "favs", "feedback", "file", "files", "film", "films", "forum", "forums", "friend", "friends", "game", "games", "gif", "go", "go.html", "go.php", "group", "groups", "help", "home", "hot", "htm", "html", "image", "images", "img", "index", "info", "intro", "item", "items", "ja", "jp", "jump", "jump.html", "jump.php", "jumping", "knowledge", "lang", "lesson", "lessons", "lib", "library", "link", "links", "list", "live", "lives", "m", "mag", "magnet", "mall", "manhua", "map", "member", "members", "message", "messages", "mobile", "movie", "movies", "music", "my", "new", "news", "note", "novel", "novels", "online", "order", "out", "out.html", "out.php", "outbound", "p", "page", "pages", "pay", "payment", "pdf", "photo", "photos", "pic", "pics", "picture", "pictures", "play", "player", "playlist", "post", "posts", "product", "products", "program", "programs", "project", "qa", "question", "rank", "ranking", "read", "readme", "redirect", "redirect.html", "redirect.php", "reg", "register", "res", "resource", "retrieve", "sale", "search", "season", "seasons", "section", "seller", "series", "service", "services", "setting", "settings", "share", "shop", "show", "shows", "site", "soft", "sort", "source", "special", "star", "stars", "static", "stock", "store", "stream", "streaming", "streams", "student", "study", "tag", "tags", "task", "teacher", "team", "tech", "temp", "test", "thread", "tool", "tools", "topic", "topics", "torrent", "trade", "travel", "tv", "txt", "type", "u", "upload", "uploads", "url", "urls", "user", "users", "v", "version", "video", "videos", "view", "vip", "vod", "watch", "web", "wenku", "wiki", "work", "www", "zh", "zh-cn", "zh-tw", "zip"]; const 随机数 = Math.floor(Math.random() * 3 + 1); const 随机路径 = 常用路径目录.sort(() => 0.5 - Math.random()).slice(0, 随机数).join('/'); return `/${随机路径}`; From b357196f14e868f4b192c0ff75e6e1bb0867fa16 Mon Sep 17 00:00:00 2001 From: cmliu Date: Sat, 20 Dec 2025 15:37:28 +0800 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20=E6=9B=B4=E6=96=B0=E5=8F=8D=E4=BB=A3?= =?UTF-8?q?=E6=95=B0=E7=BB=84=E7=B4=A2=E5=BC=95=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=BF=9E=E6=8E=A5=E5=B0=9D=E8=AF=95=E6=AC=A1?= =?UTF-8?q?=E6=95=B0=E5=92=8C=E8=BF=94=E5=9B=9E=E6=95=B0=E7=BB=84=E6=8E=92?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _worker.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/_worker.js b/_worker.js index 751d012..0e4ca12 100644 --- a/_worker.js +++ b/_worker.js @@ -1,6 +1,6 @@ import { connect } from "cloudflare:sockets"; let config_JSON, 反代IP = '', 启用SOCKS5反代 = null, 启用SOCKS5全局反代 = false, 我的SOCKS5账号 = '', parsedSocks5Address = {}; -let SOCKS5白名单 = ['*tapecontent.net', '*cloudatacdn.com', '*loadshare.org', '*cdn-centaurus.com', 'scholar.google.com']; +let SOCKS5白名单 = ['*tapecontent.net', '*cloudatacdn.com', '*loadshare.org', '*cdn-centaurus.com', 'scholar.google.com'], 反代数组索引 = 0; const Pages静态页面 = 'https://edt-pages.github.io'; ///////////////////////////////////////////////////////主程序入口/////////////////////////////////////////////// export default { @@ -502,10 +502,9 @@ async function forwardataTCP(host, portNum, rawData, ws, respHeader, remoteConnW async function connectDirect(address, port, data, 所有反代数组 = null) { let remoteSock; if (所有反代数组 && 所有反代数组.length > 0) { - const 打乱后数组 = [...所有反代数组].sort(() => Math.random() - 0.5); - const 最大尝试次数 = Math.min(8, 打乱后数组.length); - for (let i = 0; i < 最大尝试次数; i++) { - const [反代地址, 反代端口] = 打乱后数组[i]; + const 最大尝试次数 = 反代数组索引 + 8; + for (; 反代数组索引 < 最大尝试次数; 反代数组索引++) { + const [反代地址, 反代端口] = 所有反代数组[反代数组索引]; try { remoteSock = connect({ hostname: 反代地址, port: 反代端口 }); const testWriter = remoteSock.writable.getWriter(); @@ -1389,8 +1388,8 @@ async function 解析地址端口(proxyIP) { 所有反代数组 = [[地址, 端口]]; } } - - return 所有反代数组; + const 排序后数组 = 所有反代数组.sort((a, b) => a[0].localeCompare(b[0])); + return 排序后数组; // low const [选中地址, 选中端口] = 所有反代数组[Math.floor(Math.random() * 所有反代数组.length)]; return [选中地址, 选中端口]; From cb8b9e65387888c8d0c8eb45944afd52e67d5b2a Mon Sep 17 00:00:00 2001 From: cmliu Date: Sat, 20 Dec 2025 16:54:27 +0800 Subject: [PATCH 3/5] =?UTF-8?q?fix:=20=E6=9B=B4=E6=96=B0forwardataTCP?= =?UTF-8?q?=E5=92=8C=E8=A7=A3=E6=9E=90=E5=9C=B0=E5=9D=80=E7=AB=AF=E5=8F=A3?= =?UTF-8?q?=E5=87=BD=E6=95=B0=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=8E=92=E5=BA=8F?= =?UTF-8?q?=E7=A7=98=E9=92=A5=E4=BB=A5=E5=AE=9E=E7=8E=B0=E7=A1=AE=E5=AE=9A?= =?UTF-8?q?=E6=80=A7=E6=B4=97=E7=89=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _worker.js | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/_worker.js b/_worker.js index 0e4ca12..e9ede0c 100644 --- a/_worker.js +++ b/_worker.js @@ -382,7 +382,7 @@ async function 处理WS请求(request, yourUUID) { if (判断是否是木马) { const { port, hostname, rawClientData } = 解析木马请求(chunk, yourUUID); if (isSpeedTestSite(hostname)) throw new Error('Speedtest site is blocked'); - await forwardataTCP(hostname, port, rawClientData, serverSock, null, remoteConnWrapper); + await forwardataTCP(hostname, port, rawClientData, serverSock, null, remoteConnWrapper, yourUUID); } else { const { port, hostname, rawIndex, version, isUDP } = 解析魏烈思请求(chunk, yourUUID); if (isSpeedTestSite(hostname)) throw new Error('Speedtest site is blocked'); @@ -393,7 +393,7 @@ async function 处理WS请求(request, yourUUID) { const respHeader = new Uint8Array([version[0], 0]); const rawData = chunk.slice(rawIndex); if (isDnsQuery) return forwardataudp(rawData, serverSock, respHeader); - await forwardataTCP(hostname, port, rawData, serverSock, respHeader, remoteConnWrapper); + await forwardataTCP(hostname, port, rawData, serverSock, respHeader, remoteConnWrapper, yourUUID); } }, })).catch((err) => { @@ -497,7 +497,7 @@ function 解析魏烈思请求(chunk, token) { if (!hostname) return { hasError: true, message: `Invalid address: ${addressType}` }; return { hasError: false, addressType, port, hostname, isUDP, rawIndex: addrValIdx + addrLen, version }; } -async function forwardataTCP(host, portNum, rawData, ws, respHeader, remoteConnWrapper) { +async function forwardataTCP(host, portNum, rawData, ws, respHeader, remoteConnWrapper, 排序秘钥) { console.log(JSON.stringify({ configJSON: { 目标地址: host, 目标端口: portNum, 反代IP: 反代IP, 代理类型: 启用SOCKS5反代, 全局代理: 启用SOCKS5全局反代, 代理账号: 我的SOCKS5账号 } })); async function connectDirect(address, port, data, 所有反代数组 = null) { let remoteSock; @@ -530,7 +530,7 @@ async function forwardataTCP(host, portNum, rawData, ws, respHeader, remoteConnW } else if (启用SOCKS5反代 === 'http' || 启用SOCKS5反代 === 'https') { newSocket = await httpConnect(host, portNum, rawData); } else { - const 所有反代数组 = await 解析地址端口(反代IP); + const 所有反代数组 = await 解析地址端口(反代IP, 排序秘钥); newSocket = await connectDirect(atob('UFJPWFlJUC50cDEuMDkwMjI3Lnh5eg=='), 1, rawData, 所有反代数组); } remoteConnWrapper.socket = newSocket; @@ -1313,7 +1313,7 @@ function sha224(s) { return hex; } -async function 解析地址端口(proxyIP) { +async function 解析地址端口(proxyIP, 排序秘钥 = '9e2d28f7-8c1f-4965-a3d9-28f1cf4beff4') { proxyIP = proxyIP.toLowerCase(); async function DoH查询(域名, 记录类型) { try { @@ -1389,7 +1389,38 @@ async function 解析地址端口(proxyIP) { } } const 排序后数组 = 所有反代数组.sort((a, b) => a[0].localeCompare(b[0])); - return 排序后数组; + // return 排序后数组; + + // 基于种子的确定性洗牌算法 (Fisher-Yates with seeded PRNG) + function 种子随机数生成器(seed) { + // 将字符串种子转换为数字哈希 + let hash = 0; + for (let i = 0; i < seed.length; i++) { + const char = seed.charCodeAt(i); + hash = ((hash << 5) - hash) + char; + hash = hash & hash; // 转换为32位整数 + } + // 使用简单的线性同余生成器 (LCG) + return function () { + hash = (hash * 1103515245 + 12345) & 0x7fffffff; + return hash / 0x7fffffff; + }; + } + + function 确定性洗牌(数组, 种子) { + const 结果数组 = [...数组]; // 创建副本避免修改原数组 + const 随机 = 种子随机数生成器(种子); + // Fisher-Yates 洗牌算法 + for (let i = 结果数组.length - 1; i > 0; i--) { + const j = Math.floor(随机() * (i + 1)); + [结果数组[i], 结果数组[j]] = [结果数组[j], 结果数组[i]]; + } + return 结果数组; + } + + const 特殊排序后数组 = 确定性洗牌(排序后数组, 排序秘钥); + return 特殊排序后数组; + // low const [选中地址, 选中端口] = 所有反代数组[Math.floor(Math.random() * 所有反代数组.length)]; return [选中地址, 选中端口]; From f88e83eeb20b432e6eb7ff8d343880cb57e978ce Mon Sep 17 00:00:00 2001 From: cmliu Date: Sat, 20 Dec 2025 18:26:14 +0800 Subject: [PATCH 4/5] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E5=8F=8D=E4=BB=A3?= =?UTF-8?q?IP=E8=A7=A3=E6=9E=90=E9=80=BB=E8=BE=91=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E7=BC=93=E5=AD=98=E6=9C=BA=E5=88=B6=E4=BB=A5=E6=8F=90?= =?UTF-8?q?=E9=AB=98=E6=80=A7=E8=83=BD=E5=92=8C=E7=A8=B3=E5=AE=9A=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _worker.js | 199 ++++++++++++++++++++++++----------------------------- 1 file changed, 89 insertions(+), 110 deletions(-) diff --git a/_worker.js b/_worker.js index e9ede0c..a957c10 100644 --- a/_worker.js +++ b/_worker.js @@ -1,6 +1,7 @@ import { connect } from "cloudflare:sockets"; let config_JSON, 反代IP = '', 启用SOCKS5反代 = null, 启用SOCKS5全局反代 = false, 我的SOCKS5账号 = '', parsedSocks5Address = {}; -let SOCKS5白名单 = ['*tapecontent.net', '*cloudatacdn.com', '*loadshare.org', '*cdn-centaurus.com', 'scholar.google.com'], 反代数组索引 = 0; +let 缓存反代IP, 缓存反代解析数组, 缓存反代数组索引 = 0; +let SOCKS5白名单 = ['*tapecontent.net', '*cloudatacdn.com', '*loadshare.org', '*cdn-centaurus.com', 'scholar.google.com']; const Pages静态页面 = 'https://edt-pages.github.io'; ///////////////////////////////////////////////////////主程序入口/////////////////////////////////////////////// export default { @@ -498,20 +499,28 @@ function 解析魏烈思请求(chunk, token) { return { hasError: false, addressType, port, hostname, isUDP, rawIndex: addrValIdx + addrLen, version }; } async function forwardataTCP(host, portNum, rawData, ws, respHeader, remoteConnWrapper, 排序秘钥) { - console.log(JSON.stringify({ configJSON: { 目标地址: host, 目标端口: portNum, 反代IP: 反代IP, 代理类型: 启用SOCKS5反代, 全局代理: 启用SOCKS5全局反代, 代理账号: 我的SOCKS5账号 } })); + console.log(`[TCP转发] 目标: ${host}:${portNum} | 反代IP: ${反代IP} | 反代类型: ${启用SOCKS5反代 || 'proxyip'} | 全局: ${启用SOCKS5全局反代 ? '是' : '否'}`); async function connectDirect(address, port, data, 所有反代数组 = null) { let remoteSock; if (所有反代数组 && 所有反代数组.length > 0) { - const 最大尝试次数 = 反代数组索引 + 8; - for (; 反代数组索引 < 最大尝试次数; 反代数组索引++) { - const [反代地址, 反代端口] = 所有反代数组[反代数组索引]; + const 最大尝试次数 = 缓存反代数组索引 + Math.min(8, 所有反代数组.length); + for (; 缓存反代数组索引 < 最大尝试次数; 缓存反代数组索引++) { + const [反代地址, 反代端口] = 所有反代数组[缓存反代数组索引 % 所有反代数组.length]; try { + console.log(`[反代连接] 尝试连接到: ${反代地址}:${反代端口} (索引: ${缓存反代数组索引})`); remoteSock = connect({ hostname: 反代地址, port: 反代端口 }); + // 等待TCP连接真正建立,设置1秒超时 + await Promise.race([ + remoteSock.opened, + new Promise((_, reject) => setTimeout(() => reject(new Error('连接超时')), 1000)) + ]); const testWriter = remoteSock.writable.getWriter(); await testWriter.write(data); testWriter.releaseLock(); + console.log(`[反代连接] 成功连接到: ${反代地址}:${反代端口}`); return remoteSock; } catch (err) { + console.log(`[反代连接] 连接失败: ${反代地址}:${反代端口}, 错误: ${err.message}`); try { remoteSock?.close?.(); } catch (e) { } continue; } @@ -851,7 +860,7 @@ async function MD5MD5(文本) { } function 随机路径() { - const 常用路径目录 = ["#", "about", "account", "acg", "act", "activity", "ad", "ads", "ajax", "album", "albums", "anime", "api", "app", "apps", "archive", "archives", "article", "articles", "ask", "auth", "avatar", "bbs", "bd", "blog", "blogs", "book", "books", "bt", "buy", "cart", "category", "categories", "cb", "channel", "channels", "chat", "china", "city", "class", "classify", "clip", "clips", "club", "cn", "code", "collect", "collection", "comic", "comics", "community", "company", "config", "contact", "content", "course", "courses", "cp", "data", "detail", "details", "dh", "directory", "discount", "discuss", "dl", "dload", "doc", "docs", "document", "documents", "doujin", "download", "downloads", "drama", "edu", "en", "ep", "episode", "episodes", "event", "events", "f", "faq", "favorite", "favourites", "favs", "feedback", "file", "files", "film", "films", "forum", "forums", "friend", "friends", "game", "games", "gif", "go", "go.html", "go.php", "group", "groups", "help", "home", "hot", "htm", "html", "image", "images", "img", "index", "info", "intro", "item", "items", "ja", "jp", "jump", "jump.html", "jump.php", "jumping", "knowledge", "lang", "lesson", "lessons", "lib", "library", "link", "links", "list", "live", "lives", "m", "mag", "magnet", "mall", "manhua", "map", "member", "members", "message", "messages", "mobile", "movie", "movies", "music", "my", "new", "news", "note", "novel", "novels", "online", "order", "out", "out.html", "out.php", "outbound", "p", "page", "pages", "pay", "payment", "pdf", "photo", "photos", "pic", "pics", "picture", "pictures", "play", "player", "playlist", "post", "posts", "product", "products", "program", "programs", "project", "qa", "question", "rank", "ranking", "read", "readme", "redirect", "redirect.html", "redirect.php", "reg", "register", "res", "resource", "retrieve", "sale", "search", "season", "seasons", "section", "seller", "series", "service", "services", "setting", "settings", "share", "shop", "show", "shows", "site", "soft", "sort", "source", "special", "star", "stars", "static", "stock", "store", "stream", "streaming", "streams", "student", "study", "tag", "tags", "task", "teacher", "team", "tech", "temp", "test", "thread", "tool", "tools", "topic", "topics", "torrent", "trade", "travel", "tv", "txt", "type", "u", "upload", "uploads", "url", "urls", "user", "users", "v", "version", "video", "videos", "view", "vip", "vod", "watch", "web", "wenku", "wiki", "work", "www", "zh", "zh-cn", "zh-tw", "zip"]; + const 常用路径目录 = ["about", "account", "acg", "act", "activity", "ad", "ads", "ajax", "album", "albums", "anime", "api", "app", "apps", "archive", "archives", "article", "articles", "ask", "auth", "avatar", "bbs", "bd", "blog", "blogs", "book", "books", "bt", "buy", "cart", "category", "categories", "cb", "channel", "channels", "chat", "china", "city", "class", "classify", "clip", "clips", "club", "cn", "code", "collect", "collection", "comic", "comics", "community", "company", "config", "contact", "content", "course", "courses", "cp", "data", "detail", "details", "dh", "directory", "discount", "discuss", "dl", "dload", "doc", "docs", "document", "documents", "doujin", "download", "downloads", "drama", "edu", "en", "ep", "episode", "episodes", "event", "events", "f", "faq", "favorite", "favourites", "favs", "feedback", "file", "files", "film", "films", "forum", "forums", "friend", "friends", "game", "games", "gif", "go", "go.html", "go.php", "group", "groups", "help", "home", "hot", "htm", "html", "image", "images", "img", "index", "info", "intro", "item", "items", "ja", "jp", "jump", "jump.html", "jump.php", "jumping", "knowledge", "lang", "lesson", "lessons", "lib", "library", "link", "links", "list", "live", "lives", "m", "mag", "magnet", "mall", "manhua", "map", "member", "members", "message", "messages", "mobile", "movie", "movies", "music", "my", "new", "news", "note", "novel", "novels", "online", "order", "out", "out.html", "out.php", "outbound", "p", "page", "pages", "pay", "payment", "pdf", "photo", "photos", "pic", "pics", "picture", "pictures", "play", "player", "playlist", "post", "posts", "product", "products", "program", "programs", "project", "qa", "question", "rank", "ranking", "read", "readme", "redirect", "redirect.html", "redirect.php", "reg", "register", "res", "resource", "retrieve", "sale", "search", "season", "seasons", "section", "seller", "series", "service", "services", "setting", "settings", "share", "shop", "show", "shows", "site", "soft", "sort", "source", "special", "star", "stars", "static", "stock", "store", "stream", "streaming", "streams", "student", "study", "tag", "tags", "task", "teacher", "team", "tech", "temp", "test", "thread", "tool", "tools", "topic", "topics", "torrent", "trade", "travel", "tv", "txt", "type", "u", "upload", "uploads", "url", "urls", "user", "users", "v", "version", "video", "videos", "view", "vip", "vod", "watch", "web", "wenku", "wiki", "work", "www", "zh", "zh-cn", "zh-tw", "zip"]; const 随机数 = Math.floor(Math.random() * 3 + 1); const 随机路径 = 常用路径目录.sort(() => 0.5 - Math.random()).slice(0, 随机数).join('/'); return `/${随机路径}`; @@ -1313,117 +1322,87 @@ function sha224(s) { return hex; } -async function 解析地址端口(proxyIP, 排序秘钥 = '9e2d28f7-8c1f-4965-a3d9-28f1cf4beff4') { - proxyIP = proxyIP.toLowerCase(); - async function DoH查询(域名, 记录类型) { - try { - const response = await fetch(`https://1.1.1.1/dns-query?name=${域名}&type=${记录类型}`, { - headers: { 'Accept': 'application/dns-json' } - }); - if (!response.ok) return []; - const data = await response.json(); - return data.Answer || []; - } catch (error) { - console.error(`DoH查询失败 (${记录类型}):`, error); - return []; - } - } - - function 解析地址端口字符串(str) { - let 地址 = str, 端口 = 443; - if (str.includes(']:')) { - const parts = str.split(']:'); - 地址 = parts[0] + ']'; - 端口 = parseInt(parts[1], 10) || 端口; - } else if (str.includes(':') && !str.startsWith('[')) { - const colonIndex = str.lastIndexOf(':'); - 地址 = str.slice(0, colonIndex); - 端口 = parseInt(str.slice(colonIndex + 1), 10) || 端口; - } - return [地址, 端口]; - } - - let 所有反代数组 = []; - - if (proxyIP.includes('.william')) { - try { - const txtRecords = await DoH查询(proxyIP, 'TXT'); - const txtData = txtRecords.filter(r => r.type === 16).map(r => r.data); - if (txtData.length > 0) { - let data = txtData[0]; - if (data.startsWith('"') && data.endsWith('"')) data = data.slice(1, -1); - const prefixes = data.replace(/\\010/g, ',').replace(/\n/g, ',').split(',').map(s => s.trim()).filter(Boolean); - 所有反代数组 = prefixes.map(prefix => 解析地址端口字符串(prefix)); +async function 解析地址端口(proxyIP) { + if (!缓存反代IP || !缓存反代解析数组 || 缓存反代IP !== proxyIP) { + proxyIP = proxyIP.toLowerCase(); + async function DoH查询(域名, 记录类型) { + try { + const response = await fetch(`https://1.1.1.1/dns-query?name=${域名}&type=${记录类型}`, { + headers: { 'Accept': 'application/dns-json' } + }); + if (!response.ok) return []; + const data = await response.json(); + return data.Answer || []; + } catch (error) { + console.error(`DoH查询失败 (${记录类型}):`, error); + return []; } - } catch (error) { - console.error('解析William域名失败:', error); - } - } else { - let [地址, 端口] = 解析地址端口字符串(proxyIP); - - if (proxyIP.includes('.tp')) { - const tpMatch = proxyIP.match(/\.tp(\d+)/); - if (tpMatch) 端口 = parseInt(tpMatch[1], 10); } - // 判断是否是域名(非IP地址) - const ipv4Regex = /^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/; - const ipv6Regex = /^\[?([a-fA-F0-9:]+)\]?$/; + function 解析地址端口字符串(str) { + let 地址 = str, 端口 = 443; + if (str.includes(']:')) { + const parts = str.split(']:'); + 地址 = parts[0] + ']'; + 端口 = parseInt(parts[1], 10) || 端口; + } else if (str.includes(':') && !str.startsWith('[')) { + const colonIndex = str.lastIndexOf(':'); + 地址 = str.slice(0, colonIndex); + 端口 = parseInt(str.slice(colonIndex + 1), 10) || 端口; + } + return [地址, 端口]; + } - if (!ipv4Regex.test(地址) && !ipv6Regex.test(地址)) { - // 并行查询 A 和 AAAA 记录 - const [aRecords, aaaaRecords] = await Promise.all([ - DoH查询(地址, 'A'), - DoH查询(地址, 'AAAA') - ]); + let 所有反代数组 = []; - const ipv4List = aRecords.filter(r => r.type === 1).map(r => r.data); - const ipv6List = aaaaRecords.filter(r => r.type === 28).map(r => `[${r.data}]`); - const ipAddresses = [...ipv4List, ...ipv6List]; - - 所有反代数组 = ipAddresses.length > 0 - ? ipAddresses.map(ip => [ip, 端口]) - : [[地址, 端口]]; + if (proxyIP.includes('.william')) { + try { + const txtRecords = await DoH查询(proxyIP, 'TXT'); + const txtData = txtRecords.filter(r => r.type === 16).map(r => r.data); + if (txtData.length > 0) { + let data = txtData[0]; + if (data.startsWith('"') && data.endsWith('"')) data = data.slice(1, -1); + const prefixes = data.replace(/\\010/g, ',').replace(/\n/g, ',').split(',').map(s => s.trim()).filter(Boolean); + 所有反代数组 = prefixes.map(prefix => 解析地址端口字符串(prefix)); + } + } catch (error) { + console.error('解析William域名失败:', error); + } } else { - 所有反代数组 = [[地址, 端口]]; + let [地址, 端口] = 解析地址端口字符串(proxyIP); + + if (proxyIP.includes('.tp')) { + const tpMatch = proxyIP.match(/\.tp(\d+)/); + if (tpMatch) 端口 = parseInt(tpMatch[1], 10); + } + + // 判断是否是域名(非IP地址) + const ipv4Regex = /^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/; + const ipv6Regex = /^\[?([a-fA-F0-9:]+)\]?$/; + + if (!ipv4Regex.test(地址) && !ipv6Regex.test(地址)) { + // 并行查询 A 和 AAAA 记录 + const [aRecords, aaaaRecords] = await Promise.all([ + DoH查询(地址, 'A'), + DoH查询(地址, 'AAAA') + ]); + + const ipv4List = aRecords.filter(r => r.type === 1).map(r => r.data); + const ipv6List = aaaaRecords.filter(r => r.type === 28).map(r => `[${r.data}]`); + const ipAddresses = [...ipv4List, ...ipv6List]; + + 所有反代数组 = ipAddresses.length > 0 + ? ipAddresses.map(ip => [ip, 端口]) + : [[地址, 端口]]; + } else { + 所有反代数组 = [[地址, 端口]]; + } } - } - const 排序后数组 = 所有反代数组.sort((a, b) => a[0].localeCompare(b[0])); - // return 排序后数组; - - // 基于种子的确定性洗牌算法 (Fisher-Yates with seeded PRNG) - function 种子随机数生成器(seed) { - // 将字符串种子转换为数字哈希 - let hash = 0; - for (let i = 0; i < seed.length; i++) { - const char = seed.charCodeAt(i); - hash = ((hash << 5) - hash) + char; - hash = hash & hash; // 转换为32位整数 - } - // 使用简单的线性同余生成器 (LCG) - return function () { - hash = (hash * 1103515245 + 12345) & 0x7fffffff; - return hash / 0x7fffffff; - }; - } - - function 确定性洗牌(数组, 种子) { - const 结果数组 = [...数组]; // 创建副本避免修改原数组 - const 随机 = 种子随机数生成器(种子); - // Fisher-Yates 洗牌算法 - for (let i = 结果数组.length - 1; i > 0; i--) { - const j = Math.floor(随机() * (i + 1)); - [结果数组[i], 结果数组[j]] = [结果数组[j], 结果数组[i]]; - } - return 结果数组; - } - - const 特殊排序后数组 = 确定性洗牌(排序后数组, 排序秘钥); - return 特殊排序后数组; - - // low - const [选中地址, 选中端口] = 所有反代数组[Math.floor(Math.random() * 所有反代数组.length)]; - return [选中地址, 选中端口]; + 缓存反代解析数组 = 所有反代数组; + console.log(`[反代解析] 解析完成 总数: ${缓存反代解析数组.length}个\n${缓存反代解析数组.map(([ip, port], index) => `${index + 1}. ${ip}:${port}`).join('\n')}`); + 缓存反代IP = proxyIP; + } else console.log(`[反代解析] 读取缓存 总数: ${缓存反代解析数组.length}个\n${缓存反代解析数组.map(([ip, port], index) => `${index + 1}. ${ip}:${port}`).join('\n')}`); + return 缓存反代解析数组; } async function SOCKS5可用性验证(代理协议 = 'socks5', 代理参数) { From d0fae0dd6bc235d54a8a0c3863764fb6123cb960 Mon Sep 17 00:00:00 2001 From: cmliu Date: Sat, 20 Dec 2025 18:28:34 +0800 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20=E7=A7=BB=E9=99=A4forwardataTCP?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E4=B8=AD=E7=9A=84=E6=8E=92=E5=BA=8F=E7=A7=98?= =?UTF-8?q?=E9=92=A5=E5=8F=82=E6=95=B0=EF=BC=8C=E7=AE=80=E5=8C=96TCP?= =?UTF-8?q?=E8=BD=AC=E5=8F=91=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _worker.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/_worker.js b/_worker.js index a957c10..4e76bf1 100644 --- a/_worker.js +++ b/_worker.js @@ -383,7 +383,7 @@ async function 处理WS请求(request, yourUUID) { if (判断是否是木马) { const { port, hostname, rawClientData } = 解析木马请求(chunk, yourUUID); if (isSpeedTestSite(hostname)) throw new Error('Speedtest site is blocked'); - await forwardataTCP(hostname, port, rawClientData, serverSock, null, remoteConnWrapper, yourUUID); + await forwardataTCP(hostname, port, rawClientData, serverSock, null, remoteConnWrapper); } else { const { port, hostname, rawIndex, version, isUDP } = 解析魏烈思请求(chunk, yourUUID); if (isSpeedTestSite(hostname)) throw new Error('Speedtest site is blocked'); @@ -394,7 +394,7 @@ async function 处理WS请求(request, yourUUID) { const respHeader = new Uint8Array([version[0], 0]); const rawData = chunk.slice(rawIndex); if (isDnsQuery) return forwardataudp(rawData, serverSock, respHeader); - await forwardataTCP(hostname, port, rawData, serverSock, respHeader, remoteConnWrapper, yourUUID); + await forwardataTCP(hostname, port, rawData, serverSock, respHeader, remoteConnWrapper); } }, })).catch((err) => { @@ -498,7 +498,7 @@ function 解析魏烈思请求(chunk, token) { if (!hostname) return { hasError: true, message: `Invalid address: ${addressType}` }; return { hasError: false, addressType, port, hostname, isUDP, rawIndex: addrValIdx + addrLen, version }; } -async function forwardataTCP(host, portNum, rawData, ws, respHeader, remoteConnWrapper, 排序秘钥) { +async function forwardataTCP(host, portNum, rawData, ws, respHeader, remoteConnWrapper) { console.log(`[TCP转发] 目标: ${host}:${portNum} | 反代IP: ${反代IP} | 反代类型: ${启用SOCKS5反代 || 'proxyip'} | 全局: ${启用SOCKS5全局反代 ? '是' : '否'}`); async function connectDirect(address, port, data, 所有反代数组 = null) { let remoteSock; @@ -539,7 +539,7 @@ async function forwardataTCP(host, portNum, rawData, ws, respHeader, remoteConnW } else if (启用SOCKS5反代 === 'http' || 启用SOCKS5反代 === 'https') { newSocket = await httpConnect(host, portNum, rawData); } else { - const 所有反代数组 = await 解析地址端口(反代IP, 排序秘钥); + const 所有反代数组 = await 解析地址端口(反代IP); newSocket = await connectDirect(atob('UFJPWFlJUC50cDEuMDkwMjI3Lnh5eg=='), 1, rawData, 所有反代数组); } remoteConnWrapper.socket = newSocket;