Skip to content

Instantly share code, notes, and snippets.

@robertpitt
Created July 30, 2012 01:30
Show Gist options
  • Save robertpitt/3203203 to your computer and use it in GitHub Desktop.
Save robertpitt/3203203 to your computer and use it in GitHub Desktop.
SOCKS5 Server as per rfc1928 (nodejs)
/**
* 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);
@JuniorJPDJ
Copy link

thank you so much C:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment