cloudflare Workers部署vless节点代码
介绍
在一般的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();
}
}
};
客户端配置
打赏: 支付宝
本人所有文章均为技术分享,均用于防御为目的的记录,所有操作均在实验环境下进行,请勿用于其他用途,否则后果自负。 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!