Adds group functionality to TorPool

This commit is contained in:
Zachary Boyd 2018-09-10 20:53:33 -04:00
parent 94a6511fd5
commit 22908b7bba
7 changed files with 122 additions and 14 deletions

View file

@ -14,6 +14,7 @@
- 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/`
- 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
- 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.

View file

@ -144,14 +144,14 @@ class ControlServer {
}
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);
this.logger.info(`[socks]: listening on socks5://${hostname}:${port}`);
this.socksServer;
}
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);
this.logger.info(`[http]: listening on http://${hostname}:${port}`);
this.httpServer;

View file

@ -45,6 +45,100 @@ class TorPool extends EventEmitter {
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() {
if (typeof(this._default_tor_config) === 'function')
return this._default_tor_config();
@ -77,7 +171,7 @@ class TorPool extends EventEmitter {
}
get instances() {
return this._instances.slice(0).filter((tor) => tor.ready);
return this._instances.slice(0);
}
async create_instance(instance_definition) {
@ -89,10 +183,9 @@ class TorPool extends EventEmitter {
let instance_id = nanoid();
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.definition = instance_definition;
await instance.create();

View file

@ -20,16 +20,19 @@ Promise.promisifyAll(fs);
temp.track();
class TorProcess extends EventEmitter {
constructor(tor_path, config, granax_options, logger) {
constructor(tor_path, definition, granax_options, logger) {
super();
this.logger = logger || require('./winston-silent-logger');
definition.Group = definition.Group ? [].concat(definition.Group) : [];
this._definition = definition;
this.tor_path = tor_path;
this.granax_options = granax_options;
this.control_password = crypto.randomBytes(128).toString('base64');
config.DataDirectory = config.DataDirectory || temp.mkdirSync();
this.tor_config = config;
this.tor_config.DataDirectory = this.tor_config.DataDirectory || temp.mkdirSync();
}
async exit() {
@ -43,11 +46,18 @@ class TorProcess extends EventEmitter {
await p;
}
get instance_group() {
return (this.definition && this.definition.Group) || null;
}
get instance_name() {
return (this.definition && this.definition.Name) || this.process.pid;
}
get definition() { return this._definition; }
get tor_config() { return this.definition.Config; }
get dns_port() {
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()
};
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 configFile = await temp.openAsync('tor-router');

View file

@ -9,7 +9,7 @@ module.exports = {
"socksHost": null,
"dnsHost": null,
"httpHost": null,
"proxyByName": true,
"proxyByName": 'individual',
"denyUnidentifedUsers": false,
"logLevel": "info",
"loadBalanceMethod": "round_robin",

View file

@ -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_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') {
control_host = extractHost(9077);
nconf.set('controlHost', assembleHost(control_port));
@ -75,9 +78,10 @@ async function main(nconf, logger) {
}
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);
logger.info('[tor]: tor started');
}
} catch (error) {

View file

@ -9,7 +9,7 @@ require(`${__dirname}/../src/nconf_load_env.js`)(nconf);
nconf.defaults(require(`${__dirname}/../src/default_config.js`));
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 () {
this.timeout(WAIT_FOR_CREATE);