This repository has been archived on 2020-11-02. You can view files and clone it, but cannot push or open issues or pull requests.
TripSit_Suite/node_modules/socksjs/socks.js
2020-11-01 22:46:04 +00:00

329 lines
9.9 KiB
JavaScript

var stream = require('stream'),
util = require('util'),
net = require('net'),
tls = require('tls'),
ipaddr = require('ipaddr.js');
var SocksConnection = function (remote_options, socks_options) {
var that = this;
stream.Duplex.call(this);
this.remote_options = defaults(remote_options, {
host: 'localhost',
ssl: false,
rejectUnauthorized: false
});
socks_options = defaults(socks_options, {
localAddress: '0.0.0.0',
allowHalfOpen: false,
host: 'localhost',
port: 1080,
user: null,
pass: null
});
this._socksSetup = false;
this.socksAddress = null;
this.socksPort = null;
this.socksSocket = net.createConnection({
host: socks_options.host,
port: socks_options.port,
localAddress: socks_options.localAddress,
allowHalfOpen: socks_options.allowHalfOpen
}, socksConnected.bind(this, !(!socks_options.user)));
this.socksSocket.on('error', function (err) {
that.emit('error', err);
});
socksAuth.call(this, {user: socks_options.user, pass: socks_options.pass});
this.outSocket = this.socksSocket;
};
util.inherits(SocksConnection, stream.Duplex);
SocksConnection.connect = function (remote_options, socks_options, connection_listener) {
var socks_connection = new SocksConnection(remote_options, socks_options);
if (typeof connection_listener === 'function') {
socks_connection.on('connect', connection_listener);
}
return socks_connection;
};
SocksConnection.prototype._read = function () {
var data;
if (this._socksSetup) {
while ((data = this.outSocket.read()) !== null) {
if ((this.push(data)) === false) {
break;
}
}
} else {
this.push('');
}
};
SocksConnection.prototype._write = function (chunk, encoding, callback) {
if (this._socksSetup) {
this.outSocket.write(chunk, 'utf8', callback);
} else {
callback("Not connected");
}
};
SocksConnection.prototype.dispose = function () {
this.outSocket.destroy();
this.outSocket.removeAllListeners();
if (this.outSocket !== this.socksSocket) {
this.socksSocket.destroy();
this.socksSocket.removeAllListeners();
}
this.removeAllListeners();
};
var getData = function (socket, bytes, callback) {
var dataReady = function () {
var data = socket.read(bytes);
if (data !== null) {
socket.removeListener('readable', dataReady);
callback(data);
} else {
socket.on('readable', dataReady);
}
};
dataReady();
};
var socksConnected = function (auth) {
if (auth) {
this.socksSocket.write('\x05\x02\x02\x00'); // SOCKS version 5, supporting two auth methods
// username/password and 'no authentication'
} else {
this.socksSocket.write('\x05\x01\x00'); // SOCKS version 5, only supporting 'no auth' scheme
}
};
var socksAuth = function (auth) {
var that = this;
getData(this.socksSocket, 2, function (data) {
if (data.readUInt8(0) !== 5) {
that.emit('error', 'Only SOCKS version 5 is supported');
that.socksSocket.destroy();
return;
}
switch (data.readUInt8(1)) {
case 255:
that.emit('error', 'SOCKS: No acceptable authentication methods');
that.socksSocket.destroy();
return;
case 2:
that.socksSocket.write(Buffer.concat([
new Buffer([1]),
new Buffer([Buffer.byteLength(auth.user)]),
new Buffer(auth.user),
new Buffer([Buffer.byteLength(auth.pass)]),
new Buffer(auth.pass)
]));
socksAuthStatus.call(that);
break;
default:
socksRequest.call(that, that.remote_options.host, that.remote_options.port);
}
});
};
var socksAuthStatus = function (data) {
var that = this;
getData(this.socksSocket, 2, function (data) {
if (data.readUInt8(1) === 0) {
socksRequest.call(that, that.remote_options.host, that.remote_options.port);
} else {
that.emit('error', 'SOCKS: Authentication failed');
that.socksSocket.destroy();
}
});
};
var socksRequest = function (host, port) {
var header, type, hostBuf, portBuf;
if (net.isIP(host)) {
if (net.isIPv4(host)) {
type = new Buffer([1]);
} else if (net.isIPv6(host)) {
type = new Buffer([4]);
}
hostBuf = new Buffer(ipaddr.parse(host).toByteArray());
} else {
type = new Buffer([3]);
hostBuf = new Buffer(host);
hostBuf = Buffer.concat([new Buffer([Buffer.byteLength(host)]), hostBuf]);
}
header = new Buffer([5, 1, 0]);
portBuf = new Buffer(2);
portBuf.writeUInt16BE(port, 0);
this.socksSocket.write(Buffer.concat([header, type, hostBuf, portBuf]));
socksReply.call(this);
};
var socksReply = function (data) {
var that = this;
getData(this.socksSocket, 4, function (data) {
var status, err, cont;
cont = function (addr, port) {
that.socksAddress = addr;
that.socksPort = port;
if (that.remote_options.ssl) {
startTLS.call(that);
} else {
proxyData.call(that);
that.emit('connect');
}
};
status = data.readUInt8(1);
if (status === 0) {
switch(data.readUInt8(3)) {
case 1:
getData(that.socksSocket, 6, function (data2) {
var addr = '', port, i;
for (i = 0; i < 4; i++) {
if (i !== 0) {
addr += '.';
}
addr += data2.readUInt8(i).toString();
}
port = data2.readUInt16BE(4);
cont(addr, port);
});
break;
case 3:
getData(that.socksSocket, 1, function (data2) {
var length = data2.readUInt8(0);
getData(that.socksSocket, length + 2, function (data3) {
var addr, port;
addr = (data3.slice(0, -2)).toString();
port = data3.readUInt16BE(length);
cont(addr, port);
});
});
break;
case 4:
getData(that.socksSocket, 18, function (data2) {
var addr = '', port, i;
for (i = 0; i < 16; i++) {
if (i !== 0) {
addr += ':';
}
addr += data2.readUInt8(i);
}
port = data2.readUInt16BE(16);
cont(addr, port);
});
break;
default:
that.emit('error', "Invalid address type");
that.socksSocket.destroy();
break;
}
} else {
switch (status) {
case 1:
err = 'SOCKS: general SOCKS server failure';
break;
case 2:
err = 'SOCKS: Connection not allowed by ruleset';
break;
case 3:
err = 'SOCKS: Network unreachable';
break;
case 4:
err = 'SOCKS: Host unreachable';
break;
case 5:
err = 'SOCKS: Connection refused';
break;
case 6:
err = 'SOCKS: TTL expired';
break;
case 7:
err = 'SOCKS: Command not supported';
break;
case 8:
err = 'SOCKS: Address type not supported';
break;
default:
err = 'SOCKS: Unknown error';
}
that.emit('error', err);
}
});
};
var startTLS = function () {
var that = this;
var plaintext = tls.connect({
socket: this.socksSocket,
rejectUnauthorized: this.remote_options.rejectUnauthorized,
key: this.remote_options.key,
cert: this.remote_options.cert,
requestCert: this.remote_options.requestCert
});
plaintext.on('error', function (err) {
that.emit('error', err);
});
plaintext.on('secureConnect', function () {
that.emit('connect');
});
this.outSocket = plaintext;
this.getPeerCertificate = function(){ return plaintext.getPeerCertificate(); };
proxyData.call(this);
};
var proxyData = function () {
var that = this;
this.outSocket.on('readable', function () {
var data;
while ((data = that.outSocket.read()) !== null) {
if ((that.push(data)) === false) {
break;
}
}
});
this.outSocket.on('end', function () {
that.push(null);
});
this.outSocket.on('close', function (had_err) {
that.emit('close', had_err);
});
this._socksSetup = true;
};
var defaults = function(obj) {
Array.prototype.slice.call(arguments, 1).forEach(function(source) {
if (source) {
for (var prop in source) {
if (obj[prop] === null) {
obj[prop] = source[prop];
}
}
}
});
return obj;
};
module.exports = SocksConnection;