Skip to content

Instantly share code, notes, and snippets.

@adammw
Created February 14, 2012 16:21
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adammw/1827875 to your computer and use it in GitHub Desktop.
Save adammw/1827875 to your computer and use it in GitHub Desktop.
Socks library snippet for Node.js
var net = require('net'),
util = require('util'),
EventEmitter = require('events').EventEmitter;
var SOCKSClient = module.exports = function() {
var args = normalizeConnectArgs(arguments);
this.state = 'connecting';
this.socket = net.connect(args[0].port, args[0].host, onConnect.bind(this));
this.socket.on('data', onData.bind(this));
this.socket.on('end', onEnd.bind(this));
args[1] && this.on('connect', args[1]);
return this;
};
util.inherits(SOCKSClient, EventEmitter);
SOCKSClient.prototype.connect = SOCKSClient.prototype.createConnection = function() {
if (this.state != "authenticated") return;
var args = normalizeConnectArgs(arguments);
this.virtualSocket = new SOCKSSocket(this, args[0].host, args[0].port, args[1]);
return this.virtualSocket;
}
var SOCKSSocket = function(client, host, port, cb) {
this.host = host;
this.port = port;
this.client = client;
cb && this.on('connect', cb);
var portBuf = new Buffer(2);
portBuf.writeUInt16BE(port, 0);
var lenBuf = new Buffer(1);
lenBuf.writeUInt8(Buffer.byteLength(host), 0);
this.client.socket.write("\x05\x01\x00\x03" + lenBuf.toString() + host + portBuf.toString());
this.client.state = "connectrequest";
}
util.inherits(SOCKSSocket, EventEmitter);
SOCKSSocket.prototype.setEncoding = function(enc) { this.encoding = enc; }
SOCKSSocket.prototype.write = function(data) { this.client.socket.write(data); };
SOCKSSocket.prototype.end = function() { this.client.socket.end(); };
var normalizeConnectArgs = function(args) {
var options = {};
if (typeof args[0] === 'object') {
// connect(options, [cb])
options = args[0];
} else {
// connect(port, [host], [cb])
options.port = args[0];
if (typeof args[1] === 'string') {
options.host = args[1];
}
}
var cb = args[args.length - 1];
return (typeof cb === 'function') ? [options, cb] : [options];
};
var onConnect = function() {
this.socket.write("\x05\x01\x00"); // We only support the "no authentication" (0x00) scheme
this.state = "connected";
};
var onData = function(data) {
switch (this.state) {
case "connected":
if (data[0] == 0xff) {
console.error("socks: no acceptable authentication methods were offered");
this.socket.end();
this.state = "failed";
return;
}
this.state = "authenticated";
this.emit("connect");
break;
case "connectrequest":
if (data[1] != 0x00) {
console.error("socks: connect request failed: error code " + data[1]);
this.socket.end();
this.state = "failed";
return;
}
this.virtualSocket.emit("connect");
this.state = "socketconnected";
break;
case "socketconnected":
if (this.virtualSocket.encoding) data = data.toString(this.virtualSocket.encoding);
this.virtualSocket.emit("data", data);
break;
case "authenticated":
default:
console.log(this.state, data);
}
};
var onEnd = function() {
this.virtualSocket && this.virtualSocket.emit('end');
this.emit('end');
};
//test
/*
var socks = new SOCKSClient(9050, function() {
console.log('connected to socks');
var socket = socks.connect(80, 'www.google.com', function() {
console.log('connected to host');
socket.write("GET / HTTP/1.0\r\n\r\n");
socket.setEncoding('utf8');
socket.on('data', function(data) {
console.log(data);
});
});
}); //*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment