socks: refactor errors.
This commit is contained in:
parent
3a0b7d07d3
commit
5288e8a619
203
lib/net/socks.js
203
lib/net/socks.js
@ -52,6 +52,21 @@ SOCKS.states = {
|
||||
RESOLVE_DONE: 7
|
||||
};
|
||||
|
||||
SOCKS.statesByVal = util.revMap(SOCKS.states);
|
||||
|
||||
SOCKS.errors = [
|
||||
'',
|
||||
'General failure',
|
||||
'Connection not allowed',
|
||||
'Network is unreachable',
|
||||
'Host is unreachable',
|
||||
'Connection refused',
|
||||
'TTL expired',
|
||||
'Command not supported',
|
||||
'Address type not supported',
|
||||
'Unknown proxy error'
|
||||
];
|
||||
|
||||
SOCKS.prototype.error = function error(msg) {
|
||||
if (this.destroyed)
|
||||
return;
|
||||
@ -66,6 +81,13 @@ SOCKS.prototype.error = function error(msg) {
|
||||
this.destroy();
|
||||
};
|
||||
|
||||
SOCKS.prototype.getError = function getError(code) {
|
||||
if (code >= SOCKS.errors.length)
|
||||
return 'Unknown';
|
||||
|
||||
return SOCKS.errors[code];
|
||||
};
|
||||
|
||||
SOCKS.prototype.destroy = function destroy() {
|
||||
if (this.destroyed)
|
||||
return;
|
||||
@ -84,9 +106,10 @@ SOCKS.prototype.destroy = function destroy() {
|
||||
SOCKS.prototype.startTimeout = function startTimeout() {
|
||||
var self = this;
|
||||
this.timeout = setTimeout(function() {
|
||||
var state = SOCKS.statesByVal[self.state];
|
||||
self.timeout = null;
|
||||
self.error('Request timed out (' + self.state + ').');
|
||||
}, 5000);
|
||||
self.error('SOCKS request timed out (state=' + state + ').');
|
||||
}, 8000);
|
||||
};
|
||||
|
||||
SOCKS.prototype.stopTimeout = function stopTimeout() {
|
||||
@ -138,6 +161,8 @@ SOCKS.prototype.open = function open(options) {
|
||||
if (options.username != null) {
|
||||
assert(typeof options.username === 'string');
|
||||
this.username = options.username;
|
||||
assert(typeof options.password === 'string',
|
||||
'Username must have a password.');
|
||||
}
|
||||
|
||||
if (options.password != null) {
|
||||
@ -181,8 +206,11 @@ SOCKS.prototype.handleError = function handleError(err) {
|
||||
};
|
||||
|
||||
SOCKS.prototype.handleClose = function handleClose() {
|
||||
var state;
|
||||
|
||||
if (this.state !== this.target) {
|
||||
this.error('State did not reach target state of: ' + this.target);
|
||||
state = SOCKS.statesByVal[this.target];
|
||||
this.error('SOCKS request destroyed (target=' + state + ').');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -192,10 +220,10 @@ SOCKS.prototype.handleClose = function handleClose() {
|
||||
SOCKS.prototype.handleData = function handleData(data) {
|
||||
switch (this.state) {
|
||||
case SOCKS.states.INIT:
|
||||
this.error('Data before connection.');
|
||||
this.error('Data before SOCKS connection.');
|
||||
break;
|
||||
case SOCKS.states.CONNECT:
|
||||
this.error('Data before handshake.');
|
||||
this.error('Data before SOCKS handshake.');
|
||||
break;
|
||||
case SOCKS.states.HANDSHAKE:
|
||||
this.handleHandshake(data);
|
||||
@ -240,12 +268,12 @@ SOCKS.prototype.sendHandshake = function sendHandshake() {
|
||||
|
||||
SOCKS.prototype.handleHandshake = function handleHandshake(data) {
|
||||
if (data.length !== 2) {
|
||||
this.error('Bad handshake response.');
|
||||
this.error('Bad SOCKS handshake response (size).');
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[0] !== 0x05) {
|
||||
this.error('Bad SOCKS version.');
|
||||
this.error('Bad SOCKS version for handshake.');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -253,7 +281,7 @@ SOCKS.prototype.handleHandshake = function handleHandshake(data) {
|
||||
|
||||
switch (data[1]) {
|
||||
case 0xff:
|
||||
this.error('No acceptable auth methods.');
|
||||
this.error('No acceptable SOCKS auth methods.');
|
||||
break;
|
||||
case 0x02:
|
||||
this.sendAuth();
|
||||
@ -263,7 +291,7 @@ SOCKS.prototype.handleHandshake = function handleHandshake(data) {
|
||||
this.auth();
|
||||
break;
|
||||
default:
|
||||
this.error('Handshake error: ' + data[1]);
|
||||
this.error('SOCKS handshake error: ' + data[1]);
|
||||
break;
|
||||
}
|
||||
};
|
||||
@ -274,12 +302,12 @@ SOCKS.prototype.sendAuth = function sendAuth() {
|
||||
var ulen, plen, size, packet;
|
||||
|
||||
if (!user) {
|
||||
this.error('No username passed for auth.');
|
||||
this.error('No username passed for SOCKS auth.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pass) {
|
||||
this.error('No password passed for auth.');
|
||||
this.error('No password passed for SOCKS auth.');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -301,17 +329,17 @@ SOCKS.prototype.sendAuth = function sendAuth() {
|
||||
|
||||
SOCKS.prototype.handleAuth = function handleAuth(data) {
|
||||
if (data.length !== 2) {
|
||||
this.error('Bad auth response.');
|
||||
this.error('Bad packet size for SOCKS auth.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[0] !== 0x01) {
|
||||
this.error('Bad version number.');
|
||||
this.error('Bad SOCKS auth version number.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[1] !== 0x00) {
|
||||
this.error('Auth failure: ' + data[0]);
|
||||
this.error('SOCKS auth failure: ' + data[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -379,52 +407,26 @@ SOCKS.prototype.sendProxy = function sendProxy() {
|
||||
};
|
||||
|
||||
SOCKS.prototype.handleProxy = function handleProxy(data) {
|
||||
var br, len, host, port;
|
||||
var br, len, host, port, err;
|
||||
|
||||
if (data.length < 6) {
|
||||
this.error('Setup failed.');
|
||||
this.error('Bad packet size for SOCKS connect.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[0] !== 0x05) {
|
||||
this.error('Bad SOCKS version.');
|
||||
this.error('Bad SOCKS version for connect.');
|
||||
return;
|
||||
}
|
||||
|
||||
switch (data[1]) {
|
||||
case 0x00:
|
||||
break;
|
||||
case 0x01:
|
||||
this.error('General failure.');
|
||||
return;
|
||||
case 0x02:
|
||||
this.error('Connection not allowed.');
|
||||
return;
|
||||
case 0x03:
|
||||
this.error('Network is unreachable.');
|
||||
return;
|
||||
case 0x04:
|
||||
this.error('Host is unreachable.');
|
||||
return;
|
||||
case 0x05:
|
||||
this.error('Connection refused.');
|
||||
return;
|
||||
case 0x06:
|
||||
this.error('TTL expired.');
|
||||
return;
|
||||
case 0x07:
|
||||
this.error('Command not supported.');
|
||||
return;
|
||||
case 0x08:
|
||||
this.error('Address type not supported.');
|
||||
return;
|
||||
default:
|
||||
this.error('Unknown proxy error: ' + data[1]);
|
||||
return;
|
||||
if (data[1] !== 0x00) {
|
||||
err = this.getError(data[1]);
|
||||
this.error('SOCKS connect error: ' + err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[2] !== 0x00) {
|
||||
this.error('Data corruption.');
|
||||
this.error('SOCKS connect failed (padding).');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -434,7 +436,7 @@ SOCKS.prototype.handleProxy = function handleProxy(data) {
|
||||
switch (br.readU8()) {
|
||||
case 0x01:
|
||||
if (br.left() < 6) {
|
||||
this.error('Bad packet length.');
|
||||
this.error('Bad packet length for SOCKS connect.');
|
||||
return;
|
||||
}
|
||||
host = IP.toString(br.readBytes(4));
|
||||
@ -443,7 +445,7 @@ SOCKS.prototype.handleProxy = function handleProxy(data) {
|
||||
case 0x03:
|
||||
len = br.readU8();
|
||||
if (br.left() < len + 2) {
|
||||
this.error('Bad packet length.');
|
||||
this.error('Bad packet length for SOCKS connect.');
|
||||
return;
|
||||
}
|
||||
host = br.readString(len, 'utf8');
|
||||
@ -451,14 +453,14 @@ SOCKS.prototype.handleProxy = function handleProxy(data) {
|
||||
break;
|
||||
case 0x04:
|
||||
if (br.left() < 18) {
|
||||
this.error('Bad packet length.');
|
||||
this.error('Bad packet length for SOCKS connect.');
|
||||
return;
|
||||
}
|
||||
host = IP.toString(br.readBytes(16));
|
||||
port = br.readU16BE();
|
||||
break;
|
||||
default:
|
||||
this.error('Unknown response.');
|
||||
this.error('Unknown SOCKS connect response: ' + data[4]);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -466,6 +468,7 @@ SOCKS.prototype.handleProxy = function handleProxy(data) {
|
||||
this.stopTimeout();
|
||||
this.proxied = true;
|
||||
|
||||
this.emit('proxied', host, port);
|
||||
this.emit('proxy', this.socket);
|
||||
};
|
||||
|
||||
@ -488,30 +491,31 @@ SOCKS.prototype.sendResolve = function sendResolve() {
|
||||
};
|
||||
|
||||
SOCKS.prototype.handleResolve = function handleResolve(data) {
|
||||
var ip;
|
||||
var ip, err;
|
||||
|
||||
if (data.length !== 10) {
|
||||
this.error('Resolve failed.');
|
||||
this.error('Bad packet size for tor resolve.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[0] !== 0x05) {
|
||||
this.error('Bad SOCKS version.');
|
||||
this.error('Bad SOCKS version for tor resolve.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[1] !== 0x00) {
|
||||
this.error('Tor error: ' + data[1]);
|
||||
err = this.getError(data[1]);
|
||||
this.emit('Tor resolve error: ' + err + ' (' + this.name + ')');
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[2] !== 0x00) {
|
||||
this.error('Tor error: ' + data[2]);
|
||||
this.error('Unknown tor resolve error: ' + data[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[3] !== 0x01) {
|
||||
this.error('Tor error.');
|
||||
this.error('Tor resolve failed (padding).');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -550,21 +554,26 @@ SOCKS.proxy = function proxy(options) {
|
||||
/**
|
||||
* Proxy Socket
|
||||
* @constructor
|
||||
* @param {String} proxy
|
||||
* @param {String} host
|
||||
* @param {Number} port
|
||||
* @param {String?} user
|
||||
* @param {String?} pass
|
||||
*/
|
||||
|
||||
function Proxy(proxy, user, pass) {
|
||||
function Proxy(host, port, user, pass) {
|
||||
if (!(this instanceof Proxy))
|
||||
return new Proxy(proxy, user, pass);
|
||||
return new Proxy(host, port, user, pass);
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
assert(typeof host === 'string');
|
||||
assert(typeof port === 'number');
|
||||
|
||||
this.socket = null;
|
||||
this.proxy = IP.fromHostname(proxy);
|
||||
this.username = user;
|
||||
this.password = pass;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.username = user || null;
|
||||
this.password = pass || null;
|
||||
this.bytesWritten = 0;
|
||||
this.bytesRead = 0;
|
||||
this.remoteAddress = null;
|
||||
@ -577,9 +586,11 @@ Proxy.prototype.connect = co(function* connect(port, host) {
|
||||
var self = this;
|
||||
var options, socket;
|
||||
|
||||
assert(!this.socket, 'Already connected.');
|
||||
|
||||
options = {
|
||||
host: this.proxy.host,
|
||||
port: this.proxy.port,
|
||||
host: this.host,
|
||||
port: this.port,
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
destHost: host,
|
||||
@ -618,43 +629,89 @@ Proxy.prototype.connect = co(function* connect(port, host) {
|
||||
});
|
||||
|
||||
Proxy.prototype.write = function write(data, callback) {
|
||||
assert(this.socket, 'Not connected.');
|
||||
this.bytesWritten += data.length;
|
||||
return this.socket.write(data, callback);
|
||||
};
|
||||
|
||||
Proxy.prototype.end = function end() {
|
||||
assert(this.socket, 'Not connected.');
|
||||
return this.socket.end();
|
||||
};
|
||||
|
||||
Proxy.prototype.pause = function pause() {
|
||||
assert(this.socket, 'Not connected.');
|
||||
return this.socket.pause();
|
||||
};
|
||||
|
||||
Proxy.prototype.resume = function resume() {
|
||||
assert(this.socket, 'Not connected.');
|
||||
return this.socket.resume();
|
||||
};
|
||||
|
||||
Proxy.prototype.destroy = function destroy() {
|
||||
if (!this.socket)
|
||||
return;
|
||||
return this.socket.destroy();
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
function parseProxy(host) {
|
||||
var index = host.indexOf('@');
|
||||
var addr, left, right, parts;
|
||||
|
||||
if (index === -1) {
|
||||
addr = IP.fromHostname(host, 1080);
|
||||
return {
|
||||
host: addr.host,
|
||||
port: addr.port
|
||||
};
|
||||
}
|
||||
|
||||
left = host.substring(0, index);
|
||||
right = host.substring(index + 1);
|
||||
|
||||
parts = left.split(':');
|
||||
assert(parts.length > 1, 'Bad username and password.');
|
||||
|
||||
addr = IP.fromHostname(right, 1080);
|
||||
|
||||
return {
|
||||
host: addr.host,
|
||||
port: addr.port,
|
||||
username: parts[0],
|
||||
password: parts[1]
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
exports.connect = function connect(proxy, port, host, user, pass) {
|
||||
var socket = new Proxy(proxy, user, pass);
|
||||
socket.connect(port, host);
|
||||
exports.connect = function connect(proxy, destPort, destHost) {
|
||||
var addr = parseProxy(proxy);
|
||||
var host = addr.host;
|
||||
var port = addr.port;
|
||||
var user = addr.username;
|
||||
var pass = addr.password;
|
||||
var socket;
|
||||
|
||||
socket = new Proxy(host, port, user, pass);
|
||||
socket.connect(destPort, destHost);
|
||||
|
||||
return socket;
|
||||
};
|
||||
|
||||
exports.resolve = function resolve(proxy, name, user, pass) {
|
||||
var addr = IP.fromHostname(proxy);
|
||||
exports.resolve = function resolve(proxy, name) {
|
||||
var addr = parseProxy(proxy);
|
||||
return SOCKS.resolve({
|
||||
host: addr.host,
|
||||
port: addr.port,
|
||||
username: user,
|
||||
password: pass,
|
||||
username: addr.username,
|
||||
password: addr.password,
|
||||
name: name
|
||||
});
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user