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