refactor: 优化SOCKS5握手逻辑,移除UDP处理相关代码

This commit is contained in:
cmliu
2025-11-06 23:37:32 +08:00
parent 4c9b5483a0
commit f9349f9713

View File

@@ -306,44 +306,25 @@ function handleConnection(ws, request, FIXED_UUID) {
let socket, writer, reader, info;
let isFirstMsg = true, bytesReceived = 0, stallCount = 0, reconnectCount = 0;
let lastData = Date.now();
let udpStreamWrite = null, isDns = false;
const timers = {};
const dataBuffer = [];
const earlyDataHeader = request.headers.get("sec-websocket-protocol") || "";
async function 处理魏烈思握手(data) {
const bytes = new Uint8Array(data);
const 协议版本 = new Uint8Array([bytes[0], 0]);
ws.send(new Uint8Array([bytes[0], 0]));
if (Array.from(bytes.slice(1, 17)).map(n => n.toString(16).padStart(2, '0')).join('').replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, '$1-$2-$3-$4-$5') !== FIXED_UUID) throw new Error('Auth failed');
const offset1 = 18 + bytes[17] + 1;
const command = bytes[offset1];
const port = (bytes[offset1 + 1] << 8) | bytes[offset1 + 2];
const addrType = bytes[offset1 + 3];
const offset2 = offset1 + 4;
const port = (bytes[offset1] << 8) | bytes[offset1 + 1];
const addrType = bytes[offset1 + 2];
const offset2 = offset1 + 3;
const { host, length } = parseAddress(bytes, offset2, addrType === 1 ? 1 : addrType === 2 ? 2 : 4);
const payload = bytes.slice(length);
if (host.includes(atob('c3BlZWQuY2xvdWRmbGFyZS5jb20='))) throw new Error('Access');
// 判断是否为UDP命令(0x02)
const isUDP = command === 2;
if (isUDP) {
if (port === 53) {
isDns = true;
const 魏烈思响应头 = new Uint8Array([协议版本[0], 0]);
const { write } = await handleUDPOutBound(ws, 魏烈思响应头);
udpStreamWrite = write;
udpStreamWrite(payload);
return { socket: null, writer: null, reader: null, info: { host, port, isUDP: true } };
} else {
throw new Error('UDP proxy only enable for DNS which is port 53');
}
}
const sock = await createConnection(host, port);
await sock.opened;
const w = sock.writable.getWriter();
if (payload.length) await w.write(payload);
return { socket: sock, writer: w, reader: sock.readable.getReader(), info: { host, port, isUDP: false } };
return { socket: sock, writer: w, reader: sock.readable.getReader(), info: { host, port } };
}
async function 处理木马握手(data) {
@@ -353,44 +334,19 @@ function handleConnection(ws, request, FIXED_UUID) {
const socks5Data = bytes.slice(58);
if (socks5Data.byteLength < 6) throw new Error("invalid SOCKS5 request data");
const command = socks5Data[0];
// 0x01 TCP (CONNECT)
// 0x02 UDP
const isUDP = command === 2;
if (command !== 1 && command !== 2) {
throw new Error(`unsupported command ${command}, only TCP (CONNECT) and UDP are allowed`);
}
if (socks5Data[0] !== 1) throw new Error("unsupported command, only TCP (CONNECT) is allowed");
const { host, length } = parseAddress(socks5Data, 2, socks5Data[1]);
if (!host) throw new Error(`address is empty, addressType is ${socks5Data[1]}`);
if (host.includes(atob('c3BlZWQuY2xvdWRmbGFyZS5jb20='))) throw new Error('Access');
const port = (socks5Data[length] << 8) | socks5Data[length + 1];
const payload = socks5Data.slice(length + 4);
// 处理UDP DNS请求
if (isUDP) {
if (port === 53) {
isDns = true;
// 木马协议不需要响应头,直接传入空数组
const 木马响应头 = new Uint8Array(0);
const { write } = await handleUDPOutBound(ws, 木马响应头);
udpStreamWrite = write;
if (payload.length) udpStreamWrite(payload);
return { socket: null, writer: null, reader: null, info: { host, port, isUDP: true } };
} else {
throw new Error('UDP proxy only enable for DNS which is port 53');
}
}
// 处理TCP连接
const sock = await createConnection(host, port);
await sock.opened;
const w = sock.writable.getWriter();
const payload = socks5Data.slice(length + 4);
if (payload.length) await w.write(payload);
return { socket: sock, writer: w, reader: sock.readable.getReader(), info: { host, port, isUDP: false } };
return { socket: sock, writer: w, reader: sock.readable.getReader(), info: { host, port } };
}
async function createConnection(host, port) {
@@ -557,18 +513,11 @@ function handleConnection(ws, request, FIXED_UUID) {
const bytes = new Uint8Array(firstData);
if (bytes.byteLength >= 58 && bytes[56] === 0x0d && bytes[57] === 0x0a) ({ socket, writer, reader, info } = await 处理木马握手(firstData));
else ({ socket, writer, reader, info } = await 处理魏烈思握手(firstData));
// 如果是DNS UDP请求不需要启动定时器和读取循环
if (!isDns) {
startTimers();
readLoop();
}
startTimers();
readLoop();
} else {
lastData = Date.now();
// 处理UDP DNS数据
if (isDns && udpStreamWrite) {
await udpStreamWrite(evt.data);
} else if (socket && writer) {
if (socket && writer) {
await writer.write(evt.data);
} else {
dataBuffer.push(evt.data);
@@ -613,62 +562,6 @@ function parseAddress(bytes, offset, addrType) {
}
return { host, length: endOffset };
}
const WS_READY_STATE_OPEN = 1;
async function handleUDPOutBound(webSocket, 协议响应头) {
let 响应头已发送 = false;
const transformStream = new TransformStream({
start(controller) { },
transform(chunk, controller) {
// UDP消息前2字节是UDP数据的长度
for (let index = 0; index < chunk.byteLength;) {
const lengthBuffer = chunk.slice(index, index + 2);
const udpPacketLength = new DataView(lengthBuffer).getUint16(0);
const udpData = new Uint8Array(
chunk.slice(index + 2, index + 2 + udpPacketLength)
);
index = index + 2 + udpPacketLength;
controller.enqueue(udpData);
}
},
flush(controller) { }
});
// 处理DNS UDP查询
transformStream.readable.pipeTo(new WritableStream({
async write(chunk) {
const resp = await fetch('https://1.1.1.1/dns-query', {
method: 'POST',
headers: {
'content-type': 'application/dns-message',
},
body: chunk,
});
const dnsQueryResult = await resp.arrayBuffer();
const udpSize = dnsQueryResult.byteLength;
const udpSizeBuffer = new Uint8Array([(udpSize >> 8) & 0xff, udpSize & 0xff]);
if (webSocket.readyState === WS_READY_STATE_OPEN) {
console.log(`DoH success and dns message length is ${udpSize}`);
if (响应头已发送) {
webSocket.send(await new Blob([udpSizeBuffer, dnsQueryResult]).arrayBuffer());
} else {
webSocket.send(await new Blob([协议响应头, udpSizeBuffer, dnsQueryResult]).arrayBuffer());
响应头已发送 = true;
}
}
}
})).catch((error) => {
console.error('DNS UDP has error:', error);
});
const writer = transformStream.writable.getWriter();
return {
write(chunk) {
writer.write(chunk);
}
};
}
////////////////////////////////SOCKS5/HTTP函数///////////////////////////////////////////////
async function httpConnect(addressRemote, portRemote) {
const { username, password, hostname, port } = parsedSocks5Address;