Adds group functionality to TorPool
This commit is contained in:
parent
94a6511fd5
commit
22908b7bba
|
@ -14,6 +14,7 @@
|
||||||
- The `Config` property of instance definitions will now inherit from `TorPool.default_tor_config`.
|
- The `Config` property of instance definitions will now inherit from `TorPool.default_tor_config`.
|
||||||
- The mocha test has been split into individual files all under `test/`
|
- The mocha test has been split into individual files all under `test/`
|
||||||
- DNS shows the source/destination hostname/port in logs instead of what the query was resolved to
|
- DNS shows the source/destination hostname/port in logs instead of what the query was resolved to
|
||||||
|
- `TorProcess` takes an instance definition as the second argument in its constructor.
|
||||||
|
|
||||||
### Removes
|
### Removes
|
||||||
- The `new_ips` and `new_ip_at` TorPool and `new_ip` TorProcess have been removed. Use `new_identites`, `new_identity_at` and `new_identity` instead.
|
- The `new_ips` and `new_ip_at` TorPool and `new_ip` TorProcess have been removed. Use `new_identites`, `new_identity_at` and `new_identity` instead.
|
||||||
|
|
|
@ -144,14 +144,14 @@ class ControlServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
async createSOCKSServer(port, hostname) {
|
async createSOCKSServer(port, hostname) {
|
||||||
this.socksServer = new SOCKSServer(this.torPool, this.logger, (this.nconf.get('proxyByName') ? { deny_unidentified_users: this.nconf.get('denyUnidentifedUsers') } : ""));
|
this.socksServer = new SOCKSServer(this.torPool, this.logger, (this.nconf.get('proxyByName') ? { mode: this.nconf.get('proxyByName'), deny_unidentified_users: this.nconf.get('denyUnidentifedUsers') } : ""));
|
||||||
await this.socksServer.listen(port || default_ports.socks, hostname);
|
await this.socksServer.listen(port || default_ports.socks, hostname);
|
||||||
this.logger.info(`[socks]: listening on socks5://${hostname}:${port}`);
|
this.logger.info(`[socks]: listening on socks5://${hostname}:${port}`);
|
||||||
this.socksServer;
|
this.socksServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createHTTPServer(port, hostname) {
|
async createHTTPServer(port, hostname) {
|
||||||
this.httpServer = new HTTPServer(this.torPool, this.logger, (this.nconf.get('proxyByName') ? { deny_unidentified_users: this.nconf.get('denyUnidentifedUsers') } : ""));
|
this.httpServer = new HTTPServer(this.torPool, this.logger, (this.nconf.get('proxyByName') ? { mode: this.nconf.get('proxyByName'), deny_unidentified_users: this.nconf.get('denyUnidentifedUsers') } : ""));
|
||||||
await this.httpServer.listen(port || default_ports.http, hostname);
|
await this.httpServer.listen(port || default_ports.http, hostname);
|
||||||
this.logger.info(`[http]: listening on http://${hostname}:${port}`);
|
this.logger.info(`[http]: listening on http://${hostname}:${port}`);
|
||||||
this.httpServer;
|
this.httpServer;
|
||||||
|
|
|
@ -45,6 +45,100 @@ class TorPool extends EventEmitter {
|
||||||
this.granax_options = granax_options;
|
this.granax_options = granax_options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get group_names() {
|
||||||
|
return _.uniq(_.flatten(this.instances.map((instance) => instance.instance_group).filter(Boolean)));
|
||||||
|
}
|
||||||
|
|
||||||
|
add_instance_to_group(group, instance) {
|
||||||
|
instance.definition.Group = _.union(instance.instance_group, [group]);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_instance_to_group_by_name(group, instance_name) {
|
||||||
|
let instance = this.instance_by_name(instance_name);
|
||||||
|
|
||||||
|
if (!instance) throw new Error(`Instance "${instance_name}" not found`);
|
||||||
|
|
||||||
|
return this.add_instance_to_group(group, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_instance_to_group_at(group, instance_index) {
|
||||||
|
let instance = this.instance_at(instance_index);
|
||||||
|
|
||||||
|
if (!instance) throw new Error(`Instance at "${instance_index}" not found`);
|
||||||
|
|
||||||
|
return this.add_instance_to_group(group, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_instance_from_group(group, instance) {
|
||||||
|
_.remove(instance.definition.Group, (g) => g === group);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_instance_from_group_by_name(group, instance_name) {
|
||||||
|
let instance = this.instance_by_name(instance_name);
|
||||||
|
|
||||||
|
if (!instance) throw new Error(`Instance "${instance_name}" not found`);
|
||||||
|
|
||||||
|
return this.remove_instance_from_group(group, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_instance_from_group_at(group, instance_index) {
|
||||||
|
let instance = this.instance_at(instance_index);
|
||||||
|
|
||||||
|
if (!instance) throw new Error(`Instance at "${instance_index}" not found`);
|
||||||
|
|
||||||
|
return this.remove_instance_from_group(group, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
get groups() {
|
||||||
|
let groupHandler = {
|
||||||
|
get: (instances, prop) => {
|
||||||
|
if (!Number.isNaN(Number(prop)))
|
||||||
|
return instances[prop];
|
||||||
|
|
||||||
|
let { group_name } = instances;
|
||||||
|
|
||||||
|
if (prop === 'add')
|
||||||
|
return (instance) => {
|
||||||
|
return this.add_instance_to_group(group_name, instance);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (prop === 'add_by_name')
|
||||||
|
return this.add_instance_to_group_by_name.bind(this, group_name);
|
||||||
|
|
||||||
|
if (prop === 'remove')
|
||||||
|
return (instance) => {
|
||||||
|
return this.remove_instance_from_group(group_name, instance);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (prop === 'remove_by_name')
|
||||||
|
return this.remove_instance_from_group_by_name.bind(this, group_name);
|
||||||
|
|
||||||
|
if (prop === 'remove_at')
|
||||||
|
return (instance_index) => {
|
||||||
|
return this.remove_instance_from_group(group_name, instances[instance_index]);
|
||||||
|
};
|
||||||
|
|
||||||
|
return void(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let groupsHandler = {
|
||||||
|
get: (group_names, prop) => {
|
||||||
|
let instances_in_group = [];
|
||||||
|
|
||||||
|
if (group_names.indexOf(prop) !== -1) {
|
||||||
|
instances_in_group = this.instances.filter((instance) => instance.instance_group.indexOf(prop) !== -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
instances_in_group.group_name = prop;
|
||||||
|
|
||||||
|
return new Proxy(instances_in_group, groupHandler);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Proxy(this.group_names, groupsHandler);
|
||||||
|
}
|
||||||
|
|
||||||
get default_tor_config() {
|
get default_tor_config() {
|
||||||
if (typeof(this._default_tor_config) === 'function')
|
if (typeof(this._default_tor_config) === 'function')
|
||||||
return this._default_tor_config();
|
return this._default_tor_config();
|
||||||
|
@ -77,7 +171,7 @@ class TorPool extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
get instances() {
|
get instances() {
|
||||||
return this._instances.slice(0).filter((tor) => tor.ready);
|
return this._instances.slice(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
async create_instance(instance_definition) {
|
async create_instance(instance_definition) {
|
||||||
|
@ -89,10 +183,9 @@ class TorPool extends EventEmitter {
|
||||||
let instance_id = nanoid();
|
let instance_id = nanoid();
|
||||||
instance_definition.Config.DataDirectory = instance_definition.Config.DataDirectory || path.join(this.data_directory, (instance_definition.Name || instance_id));
|
instance_definition.Config.DataDirectory = instance_definition.Config.DataDirectory || path.join(this.data_directory, (instance_definition.Name || instance_id));
|
||||||
|
|
||||||
let instance = new TorProcess(this.tor_path, instance_definition.Config, this.granax_options, this.logger);
|
let instance = new TorProcess(this.tor_path, instance_definition, this.granax_options, this.logger);
|
||||||
|
|
||||||
instance.id = instance_id;
|
instance.id = instance_id;
|
||||||
instance.definition = instance_definition;
|
|
||||||
|
|
||||||
await instance.create();
|
await instance.create();
|
||||||
|
|
||||||
|
|
|
@ -20,16 +20,19 @@ Promise.promisifyAll(fs);
|
||||||
temp.track();
|
temp.track();
|
||||||
|
|
||||||
class TorProcess extends EventEmitter {
|
class TorProcess extends EventEmitter {
|
||||||
constructor(tor_path, config, granax_options, logger) {
|
constructor(tor_path, definition, granax_options, logger) {
|
||||||
super();
|
super();
|
||||||
this.logger = logger || require('./winston-silent-logger');
|
this.logger = logger || require('./winston-silent-logger');
|
||||||
|
|
||||||
|
definition.Group = definition.Group ? [].concat(definition.Group) : [];
|
||||||
|
|
||||||
|
this._definition = definition;
|
||||||
|
|
||||||
this.tor_path = tor_path;
|
this.tor_path = tor_path;
|
||||||
this.granax_options = granax_options;
|
this.granax_options = granax_options;
|
||||||
this.control_password = crypto.randomBytes(128).toString('base64');
|
this.control_password = crypto.randomBytes(128).toString('base64');
|
||||||
|
|
||||||
config.DataDirectory = config.DataDirectory || temp.mkdirSync();
|
this.tor_config.DataDirectory = this.tor_config.DataDirectory || temp.mkdirSync();
|
||||||
|
|
||||||
this.tor_config = config;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async exit() {
|
async exit() {
|
||||||
|
@ -43,11 +46,18 @@ class TorProcess extends EventEmitter {
|
||||||
await p;
|
await p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get instance_group() {
|
||||||
|
return (this.definition && this.definition.Group) || null;
|
||||||
|
}
|
||||||
|
|
||||||
get instance_name() {
|
get instance_name() {
|
||||||
return (this.definition && this.definition.Name) || this.process.pid;
|
return (this.definition && this.definition.Name) || this.process.pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get definition() { return this._definition; }
|
||||||
|
|
||||||
|
get tor_config() { return this.definition.Config; }
|
||||||
|
|
||||||
get dns_port() {
|
get dns_port() {
|
||||||
return this._dns_port || null;
|
return this._dns_port || null;
|
||||||
}
|
}
|
||||||
|
@ -109,7 +119,7 @@ class TorProcess extends EventEmitter {
|
||||||
HashedControlPassword: shell.exec(`${this.tor_path} --quiet --hash-password "${this.control_password}"`, { async: false, silent: true }).stdout.trim()
|
HashedControlPassword: shell.exec(`${this.tor_path} --quiet --hash-password "${this.control_password}"`, { async: false, silent: true }).stdout.trim()
|
||||||
};
|
};
|
||||||
|
|
||||||
let config = _.extend(_.extend({}, this.tor_config), options);
|
let config = _.extend(_.cloneDeep(this.tor_config), options);
|
||||||
let text = Object.keys(config).map((key) => `${key} ${config[key]}`).join(os.EOL);
|
let text = Object.keys(config).map((key) => `${key} ${config[key]}`).join(os.EOL);
|
||||||
|
|
||||||
let configFile = await temp.openAsync('tor-router');
|
let configFile = await temp.openAsync('tor-router');
|
||||||
|
|
|
@ -9,7 +9,7 @@ module.exports = {
|
||||||
"socksHost": null,
|
"socksHost": null,
|
||||||
"dnsHost": null,
|
"dnsHost": null,
|
||||||
"httpHost": null,
|
"httpHost": null,
|
||||||
"proxyByName": true,
|
"proxyByName": 'individual',
|
||||||
"denyUnidentifedUsers": false,
|
"denyUnidentifedUsers": false,
|
||||||
"logLevel": "info",
|
"logLevel": "info",
|
||||||
"loadBalanceMethod": "round_robin",
|
"loadBalanceMethod": "round_robin",
|
||||||
|
|
|
@ -31,6 +31,9 @@ async function main(nconf, logger) {
|
||||||
let control_host = typeof(nconf.get('controlHost')) !== 'boolean' ? extractHost(nconf.get('controlHost')) : nconf.get('controlHost');
|
let control_host = typeof(nconf.get('controlHost')) !== 'boolean' ? extractHost(nconf.get('controlHost')) : nconf.get('controlHost');
|
||||||
let control_host_ws = typeof(nconf.get('websocketControlHost')) !== 'boolean' ? extractHost(nconf.get('websocketControlHost')) : nconf.get('websocketControlHost');
|
let control_host_ws = typeof(nconf.get('websocketControlHost')) !== 'boolean' ? extractHost(nconf.get('websocketControlHost')) : nconf.get('websocketControlHost');
|
||||||
|
|
||||||
|
if (nconf.get('proxyByName') && nconf.get('proxyByName') === true)
|
||||||
|
nconf.set('proxyByName', 'individual');
|
||||||
|
|
||||||
if (typeof(control_host) === 'boolean') {
|
if (typeof(control_host) === 'boolean') {
|
||||||
control_host = extractHost(9077);
|
control_host = extractHost(9077);
|
||||||
nconf.set('controlHost', assembleHost(control_port));
|
nconf.set('controlHost', assembleHost(control_port));
|
||||||
|
@ -75,7 +78,8 @@ async function main(nconf, logger) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instances) {
|
if (instances) {
|
||||||
logger.info(`[tor]: starting ${Array.isArray(instances) ? instances.length : instances} tor instance(s)...`)
|
logger.info(`[tor]: starting ${Array.isArray(instances) ? instances.length : instances} tor instance(s)...`);
|
||||||
|
|
||||||
await control.torPool.create(instances);
|
await control.torPool.create(instances);
|
||||||
|
|
||||||
logger.info('[tor]: tor started');
|
logger.info('[tor]: tor started');
|
||||||
|
|
|
@ -9,7 +9,7 @@ require(`${__dirname}/../src/nconf_load_env.js`)(nconf);
|
||||||
nconf.defaults(require(`${__dirname}/../src/default_config.js`));
|
nconf.defaults(require(`${__dirname}/../src/default_config.js`));
|
||||||
|
|
||||||
describe('TorProcess', function () {
|
describe('TorProcess', function () {
|
||||||
let tor = new TorProcess(nconf.get('torPath'), { DataDirectory: nconf.get('parentDataDirectory'), ProtocolWarnings: 0 }, null);
|
let tor = new TorProcess(nconf.get('torPath'), { Config: { DataDirectory: nconf.get('parentDataDirectory'), ProtocolWarnings: 0 } }, null);
|
||||||
describe('#create()', function () {
|
describe('#create()', function () {
|
||||||
this.timeout(WAIT_FOR_CREATE);
|
this.timeout(WAIT_FOR_CREATE);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue