介绍

在一般的cloudflare Workers部署vless节点代码的基础上添加访问伪装(客户端不需要写伪装域名),如果不满足认证条件会返回伪装域名的内容。

代码

import { connect } from "cloudflare:sockets";
    
    // 自定义变量
    const UUID = "09662162-1234-1234-1234-89CA1571A600"; // 请在此处填写你的UUID
    const WS_READY_STATE_OPEN = 1; // WebSocket连接状态常量
    const DISGUISED_DOMAIN = "https://blog.hgtrojan.com"; // 伪装域名
    const PATH = "/hello"; // 自定义的WebSocket路径
    
    // UUID验证的正则表达式
    const regex_default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;
    
    // 验证UUID格式是否正确
    function validate(uuid) {
        return typeof uuid === "string" && regex_default.test(uuid);
    }
    
    // 将字节数组转换为十六进制字符串
    const byteToHex = [];
    for (let i = 0; i < 256; ++i) {
        byteToHex.push((i + 256).toString(16).slice(1));
    }
    
    // 将Uint8Array转换为UUID字符串
    function unsafeStringify(arr, offset = 0) {
        return (
            byteToHex[arr[offset + 0]] +
            byteToHex[arr[offset + 1]] +
            byteToHex[arr[offset + 2]] +
            byteToHex[arr[offset + 3]] +
            "-" +
            byteToHex[arr[offset + 4]] +
            byteToHex[arr[offset + 5]] +
            "-" +
            byteToHex[arr[offset + 6]] +
            byteToHex[arr[offset + 7]] +
            "-" +
            byteToHex[arr[offset + 8]] +
            byteToHex[arr[offset + 9]] +
            "-" +
            byteToHex[arr[offset + 10]] +
            byteToHex[arr[offset + 11]] +
            byteToHex[arr[offset + 12]] +
            byteToHex[arr[offset + 13]] +
            byteToHex[arr[offset + 14]] +
            byteToHex[arr[offset + 15]]
        ).toLowerCase();
    }
    
    // 验证字符串化的UUID
    function stringify(arr, offset = 0) {
        const uuid = unsafeStringify(arr, offset);
        if (!validate(uuid)) {
            throw new TypeError("字符串化的UUID无效");
        }
        return uuid;
    }
    
    // 获取伪装域名内容
    async function fetchDisguisedContent() {
        try {
            const response = await fetch(DISGUISED_DOMAIN);
            return response.text();
        } catch (error) {
            console.error("获取伪装域名内容时发生错误", error);
            return "错误";
        }
    }
    
    // 处理未经授权的请求,返回伪装域名内容
    async function handleUnauthorizedRequest() {
        const disguisedContent = await fetchDisguisedContent();
        return new Response(disguisedContent, {
            status: 200,
            headers: {
                "content-type": "text/html; charset=utf-8"
            }
        });
    }
    
    // 创建可读的WebSocket流
    function makeReadableWebSocketStream(ws, earlyDataHeader, log) {
        let readableStreamCancel = false;
        return new ReadableStream({
            start(controller) {
                ws.addEventListener("message", async (e) => {
                    if (readableStreamCancel) {
                        return;
                    }
                    const vlessBuffer = e.data;
                    controller.enqueue(vlessBuffer);
                });
                ws.addEventListener("error", (e) => {
                    log("socket has error");
                    readableStreamCancel = true;
                    controller.error(e);
                });
                ws.addEventListener("close", () => {
                    try {
                        log("webSocket is close");
                        if (readableStreamCancel) {
                            return;
                        }
                        controller.close();
                    } catch (error2) {
                        log(`websocketStream can't close DUE to `, error2);
                    }
                });
                const { earlyData, error } = base64ToArrayBuffer(earlyDataHeader);
                if (error) {
                    log(`earlyDataHeader has invaild base64`);
                    safeCloseWebSocket(ws);
                    return;
                }
                if (earlyData) {
                    controller.enqueue(earlyData);
                }
            },
            pull(controller) {},
            cancel(reason) {
                log(`websocketStream is cancel DUE to `, reason);
                if (readableStreamCancel) {
                    return;
                }
                readableStreamCancel = true;
                safeCloseWebSocket(ws);
            }
        });
    }
    
    // Base64解码为ArrayBuffer
    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 };
        }
    }
    
    // 安全关闭WebSocket连接
    function safeCloseWebSocket(socket) {
        try {
            if (socket.readyState === WS_READY_STATE_OPEN) {
                socket.close();
            }
        } catch (error) {
            console.error("安全关闭WebSocket连接时发生错误", error);
        }
    }
    
    // 处理VLESS协议头部
    function processVlessHeader(vlessBuffer, userID) {
        if (vlessBuffer.byteLength < 24) {
            return {
                hasError: true,
                message: "无效的数据"
            };
        }
        const version = new Uint8Array(vlessBuffer.slice(0, 1));
        let isValidUser = false;
        let isUDP = false;
        if (stringify(new Uint8Array(vlessBuffer.slice(1, 17))) === userID) {
            isValidUser = true;
        }
        if (!isValidUser) {
            return {
                hasError: true,
                message: "用户验证失败"
            };
        }
        const optLength = new Uint8Array(vlessBuffer.slice(17, 18))[0];
        const command = new Uint8Array(vlessBuffer.slice(18 + optLength, 18 + optLength + 1))[0];
        if (command === 1) {
            // TCP
        } else if (command === 2) {
            isUDP = true;
        } else {
            return {
                hasError: true,
                message: `不支持的命令 ${command}, 01-tcp,02-udp,03-mux`
            };
        }
        const portIndex = 18 + optLength + 1;
        const portBuffer = vlessBuffer.slice(portIndex, portIndex + 2);
        const portRemote = new DataView(portBuffer).getInt16(0);
        let addressIndex = portIndex + 2;
        const addressBuffer = new Uint8Array(vlessBuffer.slice(addressIndex, addressIndex + 1));
        const addressType = addressBuffer[0];
        let addressLength = 0;
        let addressValueIndex = addressIndex + 1;
        let addressValue = "";
        switch (addressType) {
            case 1:
                addressLength = 4;
                addressValue = new Uint8Array(vlessBuffer.slice(addressValueIndex, addressValueIndex + addressLength)).join(".");
                break;
            case 2:
                addressLength = new Uint8Array(vlessBuffer.slice(addressValueIndex, addressValueIndex + 1))[0];
                addressValueIndex += 1;
                addressValue = new TextDecoder().decode(vlessBuffer.slice(addressValueIndex, addressValueIndex + addressLength));
                break;
            case 3:
                addressLength = 16;
                const dataView = new DataView(vlessBuffer.slice(addressValueIndex, addressValueIndex + addressLength));
                const ipv6 = [];
                for (let i = 0; i < 8; i++) {
                    ipv6.push(dataView.getUint16(i * 2).toString(16));
                }
                addressValue = ipv6.join(":");
                break;
            default:
                console.log(`无效的addressType: ${addressType}`);
        }
        if (!addressValue) {
            return {
                hasError: true,
                message: `addressValue为空, addressType为 ${addressType}`
            };
        }
        return {
            hasError: false,
            addressRemote: addressValue,
            portRemote,
            rawDataIndex: addressValueIndex + addressLength,
            vlessVersion: version,
            isUDP
        };
    }
    
    // Cloudflare Worker的主处理逻辑
    export default {
        async fetch(request, env, ctx) {
            let address = "";
            let portWithRandomLog = "";
            const userID = env.UUID || UUID; // 使用自定义的UUID
            const isVaildUUID = validate(userID);
            const log = (info, event) => {
                console.log(`[${address}:${portWithRandomLog}] ${info}`, event || "");
            };
    
            try {
                const upgradeHeader = request.headers.get("Upgrade");
                if (!upgradeHeader || upgradeHeader !== "websocket") {
                    return handleUnauthorizedRequest();
                }
    
                const webSocketPair = new WebSocketPair();
                const [client, webSocket] = Object.values(webSocketPair);
                const earlyDataHeader = request.headers.get("sec-websocket-protocol") || "";
                let remoteSocket = null;
                webSocket.accept();
    
                const readableWebSocketStream = makeReadableWebSocketStream(
                    webSocket,
                    earlyDataHeader,
                    log
                );
    
                let vlessResponseHeader = new Uint8Array([0, 0]);
                let remoteConnectionReadyResolve;
    
                readableWebSocketStream.pipeTo(
                    new WritableStream({
                        async write(chunk, controller) {
                            if (remoteSocket) {
                                const writer2 = remoteSocket.writable.getWriter();
                                await writer2.write(chunk);
                                writer2.releaseLock();
                                return;
                            }
                            const {
                                hasError,
                                message,
                                portRemote,
                                addressRemote,
                                rawDataIndex,
                                vlessVersion,
                                isUDP
                            } = processVlessHeader(chunk, userID);
    
                            address = addressRemote || "";
                            portWithRandomLog = `${portRemote}--${Math.random()} ${isUDP ? "udp " : "tcp "} `;
    
                            if (isUDP && portRemote !== 53) {
                                controller.error("UDP代理仅支持端口53");
                                webSocket.close();
                                return;
                            }
                            if (hasError) {
                                controller.error(message);
                                webSocket.close();
                                return;
                            }
    
                            vlessResponseHeader = new Uint8Array([vlessVersion[0], 0]);
                            const rawClientData = chunk.slice(rawDataIndex);
                            remoteSocket = connect({
                                hostname: addressRemote,
                                port: portRemote
                            });
    
                            log(`连接成功`);
                            const writer = remoteSocket.writable.getWriter();
                            await writer.write(rawClientData);
                            writer.releaseLock();
                            remoteConnectionReadyResolve(remoteSocket);
                        },
                        close() {
                            console.log(`[${address}:${portWithRandomLog}] readableWebSocketStream已关闭`);
                        },
                        abort(reason) {
                            console.log(`[${address}:${portWithRandomLog}] readableWebSocketStream中止`, JSON.stringify(reason));
                        }
                    })
                );
    
                (async () => {
                    await new Promise((resolve) => remoteConnectionReadyResolve = resolve);
                    let count = 0;
                    // @ts-ignore
                    remoteSocket.readable.pipeTo(
                        new WritableStream({
                            start() {
                                if (webSocket.readyState === WS_READY_STATE_OPEN) {
                                    webSocket.send(vlessResponseHeader);
                                }
                            },
                            async write(chunk, controller) {
                                if (webSocket.readyState === WS_READY_STATE_OPEN) {
                                    if (count++ > 20000) {
                                        await new Promise((resolve) => setTimeout(resolve, 1));//极致并发
                                    }
                                    webSocket.send(chunk);
                                } else {
                                    controller.error("webSocket.readyState未打开,可能已关闭");
                                }
                            },
                            close() {
                                console.log(`[${address}:${portWithRandomLog}] remoteConnection!.readable已关闭`);
                            },
                            abort(reason) {
                                console.error(`[${address}:${portWithRandomLog}] remoteConnection!.readable中止`, reason);
                            }
                        })
                    ).catch((error) => {
                        console.error(`[${address}:${portWithRandomLog}] processWebSocket发生异常`, error.stack || error);
                        safeCloseWebSocket(webSocket);
                    });
                })();
    
                return new Response(null, {
                    status: 101,
                    webSocket: client
                });
            } catch (error) {
                console.error("处理请求时发生错误", error);
                return handleUnauthorizedRequest();
            }
        }
    };

客户端配置

2024-09-03T15:30:40.png

文章目录