本文实现的服务仅适用于内网测试等临时用途,请务必设置允许的客户端IP地址,防止被滥用。
SOCKS5 协议
SOCKS5是一种网络代理协议,相较于HTTP代理,它能更好地支持TCP/UDP协议的全流量转发。其工作流程分为三个阶段:
- 协议版本协商
- 认证方式协商(支持无认证)
- 请求转发处理
核心实现代码
我太懒了,实在不想长篇大论的写实现过程,还是劳烦需要的看官,自行阅读代码注释吧。
const net = require('net');
const dns = require('dns');
const SERVER_PORT = 6532;
const ALLOWED_IP = '10.211.55.1';
const server = net.createServer((clientSocket) => {
// 检查来源 IP,仅允许指定 IP 连接
const remoteAddress = clientSocket.remoteAddress.replace(/^::ffff:/, '');
if (remoteAddress !== ALLOWED_IP) {
console.log('reject client', remoteAddress);
clientSocket.destroy();
return;
}
// SOCKS5 握手阶段
clientSocket.once('data', (data) => {
// 检查 SOCKS5 协议版本
if (data[0] !== 0x05) {
clientSocket.end();
return;
}
// 仅支持无认证方式
clientSocket.write(Buffer.from([0x05, 0x00]));
// SOCKS5 请求阶段
clientSocket.once('data', (req) => {
// 只支持 CONNECT 命令
if (req[0] !== 0x05 || req[1] !== 0x01) {
clientSocket.end(Buffer.from([0x05, 0x07, 0x00, 0x01, 0, 0, 0, 0, 0, 0])); // 不支持的命令
return;
}
let addr, port, offset = 4;
const atyp = req[3];
if (atyp === 0x01) { // IPv4 地址
addr = req.slice(offset, offset + 4).join('.');
offset += 4;
} else if (atyp === 0x03) { // 域名
const len = req[offset];
addr = req.slice(offset + 1, offset + 1 + len).toString();
offset += 1 + len;
} else if (atyp === 0x04) { // IPv6 地址
addr = req.slice(offset, offset + 16);
offset += 16;
} else {
clientSocket.end(Buffer.from([0x05, 0x08, 0x00, 0x01, 0, 0, 0, 0, 0, 0])); // 不支持的地址类型
return;
}
port = req.readUInt16BE(offset);
// 建立到目标服务器的连接
const connectToTarget = (ip) => {
const remoteSocket = net.connect(port, ip, () => {
// 回复客户端连接成功
const resp = Buffer.from([0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0]);
clientSocket.write(resp);
// 数据双向转发
clientSocket.pipe(remoteSocket);
remoteSocket.pipe(clientSocket);
});
remoteSocket.on('error', () => {
clientSocket.end(Buffer.from([0x05, 0x05, 0x00, 0x01, 0, 0, 0, 0, 0, 0])); // 连接被拒绝
});
};
if (atyp === 0x03) {
// 域名需先解析
dns.lookup(addr, (err, address) => {
if (err) {
clientSocket.end(Buffer.from([0x05, 0x04, 0x00, 0x01, 0, 0, 0, 0, 0, 0])); // 主机不可达
return;
}
connectToTarget(address);
});
} else {
connectToTarget(addr);
}
});
});
clientSocket.on('error', () => { });
});
server.listen(SERVER_PORT, () => {
console.log(`SOCKS5 proxy server listening on port ${SERVER_PORT}`);
});
评论列表 (0条):
加载更多评论 Loading...