Skip to content

Instantly share code, notes, and snippets.

@chunpu
Created June 24, 2014 11:21
Show Gist options
  • Save chunpu/61fa3e46fafdd2ec013a to your computer and use it in GitHub Desktop.
Save chunpu/61fa3e46fafdd2ec013a to your computer and use it in GitHub Desktop.
useful socks!
// copy from https://github.com/gvangool/node-socks
// it is useful but shit code style
// save and one day pretty it
var net = require('net'),
util = require('util'),
log = function(args) {
//console.log(args);
},
info = console.info,
errorLog = console.error,
clients = [],
SOCKS_VERSION = 5,
/*
* Authentication methods
************************
* o X'00' NO AUTHENTICATION REQUIRED
* o X'01' GSSAPI
* o X'02' USERNAME/PASSWORD
* o X'03' to X'7F' IANA ASSIGNED
* o X'80' to X'FE' RESERVED FOR PRIVATE METHODS
* o X'FF' NO ACCEPTABLE METHODS
*/
AUTHENTICATION = {
NOAUTH: 0x00,
GSSAPI: 0x01,
USERPASS: 0x02,
NONE: 0xFF
},
/*
* o CMD
* o CONNECT X'01'
* o BIND X'02'
* o UDP ASSOCIATE X'03'
*/
REQUEST_CMD = {
CONNECT: 0x01,
BIND: 0x02,
UDP_ASSOCIATE: 0x03
},
/*
* o ATYP address type of following address
* o IP V4 address: X'01'
* o DOMAINNAME: X'03'
* o IP V6 address: X'04'
*/
ATYP = {
IP_V4: 0x01,
DNS: 0x03,
IP_V6: 0x04
},
Address = {
read: function (buffer, offset) {
if (buffer[offset] == ATYP.IP_V4) {
return util.format('%s.%s.%s.%s', buffer[offset+1], buffer[offset+2], buffer[offset+3], buffer[offset+4]);
} else if (buffer[offset] == ATYP.DNS) {
return buffer.toString('utf8', offset+2, offset+2+buffer[offset+1]);
} else if (buffer[offset] == ATYP.IP_V6) {
return buffer.slice(buffer[offset+1], buffer[offset+1+16]);
}
},
sizeOf: function(buffer, offset) {
if (buffer[offset] == ATYP.IP_V4) {
return 4;
} else if (buffer[offset] == ATYP.DNS) {
return buffer[offset+1];
} else if (buffer[offset] == ATYP.IP_V6) {
return 16;
}
}
};
function createSocksServer(cb) {
var socksServer = net.createServer();
socksServer.on('listening', function() {
var address = socksServer.address();
info('LISTENING %s:%s', address.address, address.port);
});
socksServer.on('connection', function(socket) {
info('CONNECTED %s:%s', socket.remoteAddress, socket.remotePort);
initSocksConnection.bind(socket)(cb);
});
return socksServer;
}
//
// socket is available as this
function initSocksConnection(on_accept) {
// keep log of connected clients
clients.push(this);
// remove from clients on disconnect
this.on('end', function() {
var idx = clients.indexOf(this);
if (idx != -1) {
clients.splice(idx, 1);
}
});
this.on('error', function(e) {
errorLog('%j', e);
});
// do a handshake
this.handshake = handshake.bind(this);
this.on_accept = on_accept; // No bind. We want 'this' to be the server, like it would be for net.createServer
this.on('data', this.handshake);
}
function handshake(chunk) {
this.removeListener('data', this.handshake);
var method_count = 0;
// SOCKS Version 5 is the only support version
if (chunk[0] != SOCKS_VERSION) {
errorLog('handshake: wrong socks version: %d', chunk[0]);
this.end();
}
// Number of authentication methods
method_count = chunk[1];
this.auth_methods = [];
// i starts on 1, since we've read chunk 0 & 1 already
for (var i=2; i < method_count + 2; i++) {
this.auth_methods.push(chunk[i]);
}
log('Supported auth methods: %j', this.auth_methods);
var resp = new Buffer(2);
resp[0] = 0x05;
if (this.auth_methods.indexOf(AUTHENTICATION.NOAUTH) > -1) {
log('Handing off to handleRequest');
this.handleRequest = handleRequest.bind(this);
this.on('data', this.handleRequest);
resp[1] = AUTHENTICATION.NOAUTH;
this.write(resp);
} else {
errorLog('Unsuported authentication method -- disconnecting');
resp[1] = 0xFF;
this.end(resp);
}
}
function handleRequest(chunk) {
this.removeListener('data', this.handleRequest);
var cmd=chunk[1],
address,
port,
offset=3;
// Wrong version!
if (chunk[0] !== SOCKS_VERSION) {
this.end('%d%d', 0x05, 0x01);
errorLog('handleRequest: wrong socks version: %d', chunk[0]);
return;
} /* else if (chunk[2] == 0x00) {
this.end(util.format('%d%d', 0x05, 0x01));
errorLog('handleRequest: Mangled request. Reserved field is not null: %d', chunk[offset]);
return;
} */
address = Address.read(chunk, 3);
offset = 3 + Address.sizeOf(chunk, 3) + 2;
port = chunk.readUInt16BE(offset);
log('Request: type: %d -- to: %s:%s', chunk[1], address, port);
if (cmd == REQUEST_CMD.CONNECT) {
this.request = chunk;
this.on_accept(this, port, address, proxyReady.bind(this));
} else {
this.end('%d%d', 0x05, 0x01);
return;
}
}
function proxyReady() {
log('Indicating to the client that the proxy is ready');
// creating response
var resp = new Buffer(this.request.length);
this.request.copy(resp);
// rewrite response header
resp[0] = SOCKS_VERSION;
resp[1] = 0x00;
resp[2] = 0x00;
this.write(resp);
log('Connected to: %s:%d', resp.toString('utf8', 4, resp.length - 2), resp.readUInt16BE(resp.length - 2));
}
var socks = {
createServer: createSocksServer
}
var HOST='0.0.0.0',
PORT='8888',
server = socks.createServer(function(socket, port, address, proxy_ready) {
// Implement your own proxy here! Do encryption, tunnelling, whatever! Go flippin' mental!
// I plan to tunnel everything including SSH over an HTTP tunnel. For now, though, here is the plain proxy:
console.log('Got through the first part of the SOCKS protocol.')
var proxy = net.createConnection(port, address, proxy_ready);
proxy.on('data', function(d) {
try {
console.log('receiving ' + d.length + ' bytes from proxy');
socket.write(d);
} catch(err) {
}
});
socket.on('data', function(d) {
// If the application tries to send data before the proxy is ready, then that is it's own problem.
try {
console.log('sending ' + d.length + ' bytes to proxy');
proxy.write(d);
} catch(err) {
}
});
proxy.on('close', function(had_error) {
socket.end();
console.error('The proxy closed');
}.bind(this));
socket.on('close', function(had_error) {
if (this.proxy !== undefined) {
proxy.removeAllListeners('data');
proxy.end();
}
console.error('The application closed');
}.bind(this));
});
server.on('error', function (e) {
console.error('SERVER ERROR: %j', e);
if (e.code == 'EADDRINUSE') {
console.log('Address in use, retrying in 10 seconds...');
setTimeout(function () {
console.log('Reconnecting to %s:%s', HOST, PORT);
server.close();
server.listen(PORT, HOST);
}, 10000);
}
});
server.listen(PORT);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment