Created
July 30, 2012 01:30
-
-
Save robertpitt/3203203 to your computer and use it in GitHub Desktop.
SOCKS5 Server as per rfc1928 (nodejs)
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
/** | |
* SOCKS5 Proxy as per RFC1928 | |
* @see : http://www.ietf.org/rfc/rfc1928.txt | |
*/ | |
/** | |
* Load Dependancies | |
*/ | |
var net = require('net'), | |
bjs = require('bufferjs'), | |
zlib = require('zlib'), | |
util = require('util'); | |
/** | |
* Socket States | |
*/ | |
const _STATES = { | |
CONNECTED : 0, | |
VERIFYING : 1, | |
READY : 2, | |
PROXY : 3 | |
}; | |
const _HTTP_METHODS = [ | |
'GET', | |
'POST', | |
'UPGRADE', | |
'PUT', | |
'DELETE', | |
'HEAD', | |
'CONNECT' | |
]; | |
/** | |
* Authentication Methods | |
*/ | |
const _AUTHMETHODS = { | |
NOAUTH :1, | |
GSSAPI :2, | |
USERPASS :3 | |
}; | |
/** | |
* _ATYP | |
* @type {Object} | |
*/ | |
const _ATYP = { | |
IP_V4 : 0x01, | |
DNS : 0x03, | |
IP_V6 : 0x04 | |
} | |
/** | |
* Command Types | |
*/ | |
const _COMMANDTYPES = { | |
TCPConnect :1, | |
TCPBind :2, | |
UDPBind :3 | |
}; | |
function SOCKS5Server(port) | |
{ | |
/** | |
* Create a new server | |
*/ | |
this.server = net.createServer(); | |
/** | |
* Bind the listening and connection events | |
*/ | |
this.server.on('listening', this.onListening.bind(this)); | |
this.server.on('connection', this.onConnection.bind(this)); | |
/** | |
* Listen on the dedicate port | |
*/ | |
this.server.listen(port); | |
} | |
/** | |
* reads the address of a | |
* @param {[type]} b [description] | |
* @param {[type]} o [description] | |
* @return {[type]} [description] | |
*/ | |
SOCKS5Server.prototype.readAddress = function(b, o) | |
{ | |
if(b[o] == _ATYP.IP_V4) | |
{ | |
return util.format('%s.%s.%s.%s', b[o+1], b[o+2], b[o+3], b[o+4]); | |
} | |
if(b[o] == _ATYP.DNS) | |
{ | |
return b.toString('utf8',o+2,o+2+b[o+1]); | |
} | |
if(b[o] == _ATYP.IP_V6) | |
{ | |
return b.slice(b[o+1], b[o+1+16]); | |
} | |
} | |
SOCKS5Server.prototype.sizeOf = function(b, o) | |
{ | |
if(b[o] == _ATYP.IP_V4) | |
{ | |
return 4; | |
} | |
if(b[o] == _ATYP.DNS) { | |
return b[o+1]; | |
} | |
if(b[o] == _ATYP.IP_V6) { | |
return 16; | |
} | |
} | |
SOCKS5Server.prototype.onListening = function() | |
{ | |
console.log('SOCKS5 LISTENING on %s:%s', this.server.address().address, this.server.address().port); | |
} | |
SOCKS5Server.prototype.onConnection = function(socket) | |
{ | |
console.log('SOCKS5 Connection %s:%s', socket.remoteAddress, socket.remotePort); | |
/** | |
* Expect the first packet to be a handshake | |
*/ | |
socket.on('data', function(chunk){this.onHandshake(chunk, socket);}.bind(this)); | |
} | |
SOCKS5Server.prototype.onHandshake = function(buffer, socket) | |
{ | |
/** | |
* Remove event handlers | |
*/ | |
socket._events.data = null; | |
/** | |
* Collect basic information from the socket head | |
* +----+----------+----------+ | |
* |VER | NMETHODS | METHODS | | |
* +----+----------+----------+ | |
* | 1 | 1 | 1 to 255 | | |
* +----+----------+----------+ | |
*/ | |
var _version = buffer[0]; | |
var _nmethods = buffer[1]; | |
var _methods = []; | |
/** | |
* Validate the socks request complies with V5 | |
*/ | |
if(_version !== 5) | |
{ | |
return socket.close(); | |
} | |
/** | |
* Get the methods from the packet | |
*/ | |
for(var i = 0; i < _nmethods; i++) | |
{ | |
_methods.push(buffer[i + 1]); | |
} | |
/** | |
* Create a 2bit buffer to send back to client | |
*/ | |
var responseBuffer = new Buffer(2); | |
responseBuffer[0] = 0x05; | |
/** | |
* Check for NOAUTH handling | |
*/ | |
if(_methods.indexOf(_AUTHMETHODS.NOAUTH) > -1) | |
{ | |
/** | |
* Directly pass of to the requestHandler | |
*/ | |
socket.on('data', function(chunk){this.handleRequest(chunk, socket);}.bind(this)); | |
/** | |
* Set the selected method to idx[1] | |
*/ | |
responseBuffer[1] = 0x00; | |
/** | |
* Send the buffer | |
*/ | |
socket.write(responseBuffer); | |
return; | |
} | |
/** | |
* Check for GSSAPI Authorization | |
*/ | |
//TODO | |
/** | |
* Check for USER:PASS Authentication | |
*/ | |
//TODO | |
/** | |
* Response with not implemented err. | |
*/ | |
responseBuffer[1] = 0xFF; | |
socket.end(responseBuffer); | |
} | |
SOCKS5Server.prototype.handleRequest = function(buffer, socket) | |
{ | |
socket._events.data = null; | |
/** | |
* Collect information from the request packet | |
*/ | |
var _version = buffer[0], | |
_command = buffer[1], | |
_address = this.readAddress(buffer, 3), | |
_offset = 3 + this.sizeOf(buffer, 3) + 2, | |
_port = buffer.readUInt16BE(_offset); | |
/** | |
* Check the socks version | |
*/ | |
if(_version !== 5) | |
{ | |
return socket.end('%d%d', 0x05, 0x01); | |
} | |
if (_command == _COMMANDTYPES.TCPConnect) | |
{ | |
console.log('Proxy Request: type: %d -- to: %s:%s', _command.toString(), _address, _port); | |
this.proxy(_address, _port, socket, buffer); | |
return; | |
} | |
console.log("Unknown command type (%d) for (%s:%d)", _command, _address, _port); | |
socket.end('%d%d', 0x05, 0x01); | |
} | |
SOCKS5Server.prototype.proxy = function(host, port, socket, buffer) | |
{ | |
/** | |
* Create a new socket to the requested host | |
*/ | |
var connection = net.createConnection(port, host, function(){ | |
/** | |
* Make sure the socket is writable | |
*/ | |
if(socket.writable === false || socket.readable === false) | |
{ | |
socket.close() | |
connection.close() | |
return; | |
} | |
/** | |
* Create a copy of the buffer | |
*/ | |
var nBuffer = new Buffer(buffer.length); | |
buffer.copy(nBuffer); | |
/** | |
* Set the method segment to 0x00. | |
*/ | |
nBuffer[1] = 0x00; //Success | |
/** | |
* Write back the modified buffer | |
*/ | |
socket.write(nBuffer); | |
try | |
{ | |
socket.pipe(connection); | |
connection.pipe(socket); | |
}catch(e) | |
{ | |
console.log("Pipelining issue: ", e.message); | |
socket.close(); | |
connection.close(); | |
} | |
}); | |
} | |
new SOCKS5Server(1080); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
thank you so much C: