Skip to content

Instantly share code, notes, and snippets.

@GZShi
Last active April 9, 2016 09:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GZShi/79d5f79c38251acb4f5cbb24203953a2 to your computer and use it in GitHub Desktop.
Save GZShi/79d5f79c38251acb4f5cbb24203953a2 to your computer and use it in GitHub Desktop.
根据RFC1928实现的一个sock5代理服务器,基于nodejs。已知bug:存在内存泄露
'use strict';
const net = require('net');
const local = '0.0.0.0';
const port = 1081;
var connSeq = 0;
const server = net.createServer((client) => {
var seq = connSeq++;
var uid = 1;
function log(text) {
console.log(`[${seq}]`, text);
}
function genState() {
return uid++;
}
// 基本的socket事件处理
client.on('connection', () => log(`client connected. from ${client.ip}`));
client.on('close', (data) => log(`client closed.`));
client.on('error', (err) => log(`error: ${err}`));
client.on('data', onData);
// 状态定义
const STATE_SHAKE_VER_AND_MN = genState();
const STATE_SHAKE_METHOD = genState();
const STATE_INVALID_VER_OR_MN = genState();
const STATE_INVALID_METHODS = genState();
const STATE_INVALID_CMD = genState();
const STATE_WAIT_REQUEST_HEAD = genState();
const STATE_WAIT_REQUEST_BODY = genState();
const STATE_PIPE_DATA = genState();
// 常量定义
const VER_5 = 0x05;
const METHOD_NO_AUTH = 0x00;
const METHOD_GSSAPI = 0x01;
const METHOD_NO_ACCEPT = 0xff;
// 命令定义
const CMD_CONNECT = 0x01;
const CMD_BIND = 0x02;
const CMD_UDP_ASSOCIATE = 0x03;
// 地址类型定义
const ATYP_IPV4 = 0x01;
const ATYP_IPV6 = 0x04;
const ATYP_DOMAIN = 0x03;
// 需要协商的变量
var state = STATE_SHAKE_VER_AND_MN;
var buf = new Buffer([]);
var version = 0;
var nmethods = 0;
var methods = [];
var command;
var dest = {
atype: null,
addr: null,
port: null
};
var socket = null;
// 实现规范里面提到的CONNECT命令
function connect() {
buf = new Buffer([]);
// 尝试连接
log(`try connect to dest: addr=${dest.addr} port=${dest.port}`);
socket = net.connect({host: dest.addr, port: dest.port}, () => {
state = STATE_PIPE_DATA;
var headerBuf = new Buffer([0x05, 0x00, 0x00, 0x01]);
var bindAddrBuf = new Buffer([0x00, 0x00, 0x00, 0x00]);
var bindPortBuf = new Buffer([0x00, 0x00]);
client.write(Buffer.concat([headerBuf, bindAddrBuf, bindPortBuf], 10));
client.pipe(socket);
socket.pipe(client);
});
socket.on('error', () => {
console.log('代理套接字出现错误');
client.end();
});
}
// 实现规范里面提到的BIND命令
function bind() {
// todo
unsupportedMethod();
}
// 实现规范里面提到的UDP连接命令
function udp() {
// todo
unsupportedMethod();
}
// 为支持的方法应答
function unsupportedMethod() {
state = STATE_INVALID_CMD;
buf = new Buffer([]);
client.write(new Buffer([VER_5, REP_CMD_NOT_SUPPORT, 0, ]))
}
var cmdsList = [null, connect, bind, udp];
function denies(stat) {
log('denies, stat = ${stat}');
state = stat;
buf = new Buffer([]);
client.write(new Buffer([VER_5, METHOD_NO_ACCEPT]));
}
function onData(chunk) {
if (chunk && chunk.length > 0) {
// log(`on data, length = ${chunk.length}`);
buf = Buffer.concat([buf, chunk], buf.length + chunk.length);
}
if (state === STATE_SHAKE_VER_AND_MN) {
// 第一步,客户端送过来协议版本ver与支持的方法数nmethods
if (buf.length < 2) return;
if (buf.length >= 2) {
version = buf[0];
nmethods = buf[1];
if (version === VER_5 && nmethods >= 1 && nmethods <= 255) {
state = STATE_SHAKE_METHOD;
if (buf.length > 2) {
buf = buf.slice(2 - buf.length);
onData(null);
}
} else {
return denies(STATE_INVALID_VER_OR_MN);
}
}
} else if (state === STATE_SHAKE_METHOD) {
// 第二步,根据方法数nmethods,确定接下来需要接受的数据长度
if (buf.length >= nmethods) {
let found = false;
for (let i = 0; i < nmethods; ++i) {
if (buf[i] === METHOD_NO_AUTH) {
found = true;
break;
}
}
if (!found) {
return denies(STATE_INVALID_METHODS);
}
state = STATE_WAIT_REQUEST_HEAD;
client.write(new Buffer([VER_5, METHOD_NO_AUTH]));
if (buf.length > nmethods) {
buf = buf.slice(nmethods - buf.length);
onData(null);
} else {
buf = new Buffer([]);
}
}
} else if (state === STATE_WAIT_REQUEST_HEAD) {
// 第三步,等待客户端发送请求头部,共5个字节
// VER(1)|CMD(1)|RSV(1)|ATYP(1)|LEN(1)
if (buf.length < 5) return; // 起码需要6个字节
if (buf[0] !== VER_5) return denies(STATE_INVALID_VER_OR_MN);
command = buf[1];
dest.atype = buf[3];
dest.addrLen = buf[4];
state = STATE_WAIT_REQUEST_BODY;
if (buf.length > 5) {
buf = buf.slice(5 - buf.length);
onData(null);
}
} else if (state === STATE_WAIT_REQUEST_BODY) {
// 第四步,等待客户端发送目标地址与端口
// 地址长度由dest.addrLen决定,端口长度2字节
if (buf.length < 2 + dest.addrLen) return;
var addrBuf = buf.slice(0, dest.addrLen);
var portBuf = buf.slice(dest.addrLen, dest.addrLen + 2);
dest.addr = addrBuf.toString();
dest.port = portBuf[0] * 256 + portBuf[1];
(cmdsList[+command] || unsupportedMethod)();
} else if (state === STATE_PIPE_DATA) {
// console.log(buf.toString());
}
}
});
server.listen(port, local);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment