| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- var net = require('net');
- var ip = require('ip');
- var SmartBuffer = require('smart-buffer');
- (function () {
- var COMMAND = {
- Connect: 0x01,
- Bind: 0x02,
- Associate: 0x03
- };
- var SOCKS4_RESPONSE = {
- Granted: 0x5A,
- Failed: 0x5B,
- Rejected: 0x5C,
- RejectedIdent: 0x5D
- };
- var SOCKS5_AUTH = {
- NoAuth: 0x00,
- GSSApi: 0x01,
- UserPass: 0x02
- };
- var SOCKS5_RESPONSE = {
- Granted: 0x00,
- Failure: 0x01,
- NotAllowed: 0x02,
- NetworkUnreachable: 0x03,
- HostUnreachable: 0x04,
- ConnectionRefused: 0x05,
- TTLExpired: 0x06,
- CommandNotSupported: 0x07,
- AddressNotSupported: 0x08
- };
- exports.createConnection = function (options, callback) {
- var socket = new net.Socket(), finished = false, buff = new SmartBuffer();
- // Defaults
- options.timeout = options.timeout || 10000;
- options.proxy.command = commandFromString(options.proxy.command);
- options.proxy.userid = options.proxy.userid || "";
- var auth = options.proxy.authentication || {};
- auth.username = auth.username || "";
- auth.password = auth.password || "";
- options.proxy.authentication = auth;
- // Connect & negotiation timeout
- function onTimeout() {
- finish(new Error("Connection Timed Out"), socket, null, callback);
- }
- socket.setTimeout(options.timeout, onTimeout);
- // Socket events
- socket.once('close', function () {
- finish(new Error("Socket Closed"), socket, null, callback);
- });
- socket.once('error', function (err) {
- });
- socket.once('connect', function () {
- if (options.proxy.type === 4) {
- negotiateSocks4(options, socket, callback);
- } else if (options.proxy.type === 5) {
- negotiateSocks5(options, socket, callback);
- } else {
- throw new Error("Please specify a proxy type in options.proxy.type");
- }
- });
- socket.connect(options.proxy.port, options.proxy.ipaddress);
- // 4/4a (connect, bind) - Supports domains & ipaddress
- function negotiateSocks4(options, socket, callback) {
- buff.writeUInt8(0x04);
- buff.writeUInt8(options.proxy.command);
- buff.writeUInt16BE(options.target.port);
- // ipv4 or domain?
- if (net.isIPv4(options.target.host)) {
- buff.writeBuffer(ip.toBuffer(options.target.host));
- buff.writeStringNT(options.proxy.userid);
- } else {
- buff.writeUInt8(0x00);
- buff.writeUInt8(0x00);
- buff.writeUInt8(0x00);
- buff.writeUInt8(0x01);
- buff.writeStringNT(options.proxy.userid);
- buff.writeStringNT(options.target.host);
- }
- socket.once('data', receivedResponse);
- socket.write(buff.toBuffer());
- function receivedResponse(data) {
- socket.pause();
- if (data.length === 8 && data[1] === SOCKS4_RESPONSE.Granted) {
- if (options.proxy.command === COMMAND.Bind) {
- buff.clear();
- buff.writeBuffer(data);
- buff.skip(2);
- var info = {
- port: buff.readUInt16BE(),
- host: buff.readUInt32BE()
- };
- if (info.host === 0) {
- info.host = options.proxy.ipaddress;
- } else {
- info.host = ip.fromLong(info.host);
- }
- finish(null, socket, info, callback);
- } else {
- finish(null, socket, null, callback);
- }
- } else {
- finish(new Error("Rejected (" + data[1] + ")"), socket, null, callback);
- }
- }
- }
- // Socks 5 (connect, bind, associate) - Supports domains and ipv4, ipv6.
- function negotiateSocks5(options, socket, callback) {
- buff.writeUInt8(0x05);
- buff.writeUInt8(2);
- buff.writeUInt8(SOCKS5_AUTH.NoAuth);
- buff.writeUInt8(SOCKS5_AUTH.UserPass);
- socket.once('data', handshake);
- socket.write(buff.toBuffer());
- function handshake(data) {
- if (data.length !== 2) {
- finish(new Error("Negotiation Error"), socket, null, callback);
- } else if (data[0] !== 0x05) {
- finish(new Error("Negotiation Error (invalid version)"), socket, null, callback);
- } else if (data[1] === 0xFF) {
- finish(new Error("Negotiation Error (unacceptable authentication)"), socket, null, callback);
- } else {
- if (data[1] === SOCKS5_AUTH.NoAuth) {
- sendRequest();
- } else if (data[1] === SOCKS5_AUTH.UserPass) {
- sendAuthentication(options.proxy.authentication);
- } else {
- finish(new Error("Negotiation Error (unknown authentication type)"), socket, null, callback);
- }
- }
- }
- function sendAuthentication(authinfo) {
- buff.clear();
- buff.writeUInt8(0x01);
- buff.writeUInt8(Buffer.byteLength(authinfo.username));
- buff.writeString(authinfo.username);
- buff.writeUInt8(Buffer.byteLength(authinfo.password));
- buff.writeString(authinfo.password);
- socket.once('data', authenticationResponse);
- socket.write(buff.toBuffer());
- function authenticationResponse(data) {
- if (data.length === 2 && data[1] === 0x00) {
- sendRequest();
- } else {
- finish(new Error("Negotiation Error (authentication failed)"), socket, null, callback);
- }
- }
- }
- function sendRequest() {
- buff.clear();
- buff.writeUInt8(0x05);
- buff.writeUInt8(options.proxy.command);
- buff.writeUInt8(0x00);
- // ipv4, ipv6, domain?
- if (net.isIPv4(options.target.host)) {
- buff.writeUInt8(0x01);
- buff.writeBuffer(ip.toBuffer(options.target.host));
- } else if (net.isIPv6(options.target.host)) {
- buff.writeUInt8(0x04);
- buff.writeBuffer(ip.toBuffer(options.target.host));
- } else {
- buff.writeUInt8(0x03);
- buff.writeUInt8(options.target.host.length);
- buff.writeString(options.target.host);
- }
- buff.writeUInt16BE(options.target.port);
- socket.once('data', receivedResponse);
- socket.write(buff.toBuffer());
- }
- function receivedResponse(data) {
- socket.pause();
- if (data.length < 4) {
- finish(new Error("Negotiation Error"), socket, null, callback);
- } else if (data[0] === 0x05 && data[1] === SOCKS5_RESPONSE.Granted) {
- if (options.proxy.command === COMMAND.Connect) {
- finish(null, socket, null, callback);
- } else if (options.proxy.command === COMMAND.Bind || options.proxy.command === COMMAND.Associate) {
- buff.clear();
- buff.writeBuffer(data);
- buff.skip(3);
- var info = {};
- var addrtype = buff.readUInt8();
- try {
- if (addrtype === 0x01) {
- info.host = buff.readUInt32BE();
- if (info.host === 0)
- info.host = options.proxy.ipaddress;
- else
- info.host = ip.fromLong(info.host);
- } else if (addrtype === 0x03) {
- var len = buff.readUInt8();
- info.host = buff.readString(len);
- } else if (addrtype === 0x04) {
- info.host = buff.readBuffer(16);
- } else {
- finish(new Error("Negotiation Error (invalid host address)"), socket, null, callback);
- }
- info.port = buff.readUInt16BE();
- finish(null, socket, info, callback);
- } catch (ex) {
- finish(new Error("Negotiation Error (missing data)"), socket, null, callback);
- }
- }
- } else {
- finish(new Error("Negotiation Error (" + data[1] + ")"), socket, null, callback);
- }
- }
- }
- function finish(err, socket, info, callback) {
- socket.setTimeout(0, onTimeout);
- if (!finished) {
- finished = true;
- if (buff instanceof SmartBuffer)
- buff.destroy();
- if (err && socket instanceof net.Socket) {
- socket.removeAllListeners('close');
- socket.removeAllListeners('timeout');
- socket.removeAllListeners('data');
- socket.destroy();
- socket = null;
- }
- callback(err, socket, info);
- }
- }
- function commandFromString(str) {
- var result = COMMAND.Connect;
- if (str === "connect") {
- result = COMMAND.Connect;
- } else if (str === 'associate') {
- result = COMMAND.Associate;
- } else if (str === 'bind') {
- result = COMMAND.Bind;
- }
- return result;
- }
- };
- exports.createUDPFrame = function (target, data, frame) {
- var buff = new SmartBuffer();
- buff.writeUInt16BE(0);
- buff.writeUInt8(frame || 0x00);
- if (net.isIPv4(target.host)) {
- buff.writeUInt8(0x01);
- buff.writeUInt32BE(ip.toLong(target.host));
- } else if (net.isIPv6(target.host)) {
- buff.writeUInt8(0x04);
- buff.writeBuffer(ip.toBuffer(target.host));
- } else {
- buff.writeUInt8(0x03);
- buff.writeUInt8(Buffer.byteLength(target.host));
- buff.writeString(target.host);
- }
- buff.writeUInt16BE(target.port);
- buff.writeBuffer(data);
- return buff.toBuffer();
- };
- })();
|