Last active
April 9, 2016 09:02
-
-
Save GZShi/79d5f79c38251acb4f5cbb24203953a2 to your computer and use it in GitHub Desktop.
根据RFC1928实现的一个sock5代理服务器,基于nodejs。已知bug:存在内存泄露
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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