Allows to default config of the Tor instances to be specified at startup and during runtime. Allows for the configuration of individual Tor instances to be specified at startup and during runtime

This commit is contained in:
Zachary Boyd 2018-05-09 18:15:58 -07:00
parent b3546799da
commit 369ffa5d4f
11 changed files with 254 additions and 44 deletions

View file

@ -8,7 +8,9 @@ EXPOSE 53
EXPOSE 9077 EXPOSE 9077
ENV PATH $PATH:/app/bin ENV PARENT_DATA_DIRECTORTY /var/lib/tor-router
ENV PATH $PATH:/app/bin
ADD https://deb.nodesource.com/setup_8.x /tmp/nodejs_install ADD https://deb.nodesource.com/setup_8.x /tmp/nodejs_install

View file

@ -24,6 +24,7 @@ The following command line switches and their environment variable equivalents a
|Command line switch|Environment Variable|Description| |Command line switch|Environment Variable|Description|
|-------------------|--------------------|-----------| |-------------------|--------------------|-----------|
|-f, --config | |Path to a JSON configuration file to use|
|-c, --controlPort |CONTROL_PORT |Port the control server will bind to (see below)| |-c, --controlPort |CONTROL_PORT |Port the control server will bind to (see below)|
|-j, --instances |INSTANCES |Number of Tor instances to spawn| |-j, --instances |INSTANCES |Number of Tor instances to spawn|
|-s, --socksPort |SOCKS_PORT |Port the SOCKS proxy will bind to| |-s, --socksPort |SOCKS_PORT |Port the SOCKS proxy will bind to|
@ -31,9 +32,52 @@ The following command line switches and their environment variable equivalents a
|-h, --httpPort |HTTP_PORT |Port the HTTP proxy will bind to| |-h, --httpPort |HTTP_PORT |Port the HTTP proxy will bind to|
|-l, --logLevel |LOG_LEVEL |Log level (defaults to "info") set to "null" to disable logging. To see a log of all network traffic set logLevel to "verbose"| |-l, --logLevel |LOG_LEVEL |Log level (defaults to "info") set to "null" to disable logging. To see a log of all network traffic set logLevel to "verbose"|
For example: `tor-router -j 3 -s 9050` would start the proxy with 3 tor instances and listen for SOCKS connections on 9050. For example: `tor-router -j 3 -s 9050` would start the proxy with 3 tor instances and listen for SOCKS connections on 9050.
## Configuration
Using the `--config` or `-f` command line switch you can set the path to a JSON file which can be used to load configuration on startup
The same variable names from the command line switches are used to name the keys in the JSON file.
Example:
```
{
"controlPort": 9077,
"logLevel": "debug"
}
```
Using the configuration file you can set a default configuration for all Tor instances
```
{
"torConfig": {
"MaxCircuitDirtiness": "10"
}
}
```
You can also specify a configuration for individual instances by setting the "instances" field to an array instead of an integer
```
{
"instances": [
{
"Config": {
"DataDirectory": "/tmp/my-tor-dir1"
}
},
{
"Config": {
"DataDirectory": "/tmp/my-tor-dir2"
}
}
]
}
```
## Control Server ## Control Server
A JSON-RPC 2 TCP Server will listen on port 9077 by default. Using the rpc server the client can add/remove Tor instances and get a new identity (which includes a new ip address) while Tor Router is running. A JSON-RPC 2 TCP Server will listen on port 9077 by default. Using the rpc server the client can add/remove Tor instances and get a new identity (which includes a new ip address) while Tor Router is running.

View file

@ -9,6 +9,7 @@ var DNSServer = TorRouter.DNSServer;
var TorPool = TorRouter.TorPool; var TorPool = TorRouter.TorPool;
var ControlServer = TorRouter.ControlServer; var ControlServer = TorRouter.ControlServer;
var winston = require('winston'); var winston = require('winston');
var async = require('async');
process.title = 'tor-router'; process.title = 'tor-router';
@ -56,10 +57,15 @@ let argv_config = require('yargs')
describe: 'Controls the verbosity of console log output. Default level is "info". Set to "verbose" to see all network traffic logged or "null" to disable logging completely [default: info]', describe: 'Controls the verbosity of console log output. Default level is "info". Set to "verbose" to see all network traffic logged or "null" to disable logging completely [default: info]',
demand: false demand: false
// ,default: "info" // ,default: "info"
},
p: {
alias: 'parentDataDirectory',
describe: 'Parent directory that will contain the data directories for the instances',
demand: false
} }
}); });
let env_whitelist = [ "CONTROL_PORT", "INSTANCES", "SOCKS_PORT", "DNS_PORT", "HTTP_PORT", "LOG_LEVEL", "controlPort", "instances", "socksPort", "dnsPort", "httpPort", "logLevel" ]; let env_whitelist = [ "CONTROL_PORT", "INSTANCES", "SOCKS_PORT", "DNS_PORT", "HTTP_PORT", "LOG_LEVEL", 'PARENT_DATA_DIRECTORIES', "controlPort", "instances", "socksPort", "dnsPort", "httpPort", "logLevel", 'parentDataDirectories' ];
nconf nconf
.env({ .env({
@ -110,7 +116,7 @@ let http_port = nconf.get('httpPort');
let control_port = nconf.get('controlPort'); let control_port = nconf.get('controlPort');
let control = new ControlServer(logger); let control = new ControlServer(logger, nconf);
process.on('SIGHUP', () => { process.on('SIGHUP', () => {
control.torPool.new_ips(); control.torPool.new_ips();
@ -129,7 +135,7 @@ if (dns_port) {
} }
if (instances) { if (instances) {
logger && logger.info(`[tor]: starting ${instances} tor instances...`) logger && logger.info(`[tor]: starting ${Array.isArray(instances) ? instances.length : instances} tor instances...`)
control.torPool.create(instances, (err) => { control.torPool.create(instances, (err) => {
logger && logger.info('[tor]: tor started'); logger && logger.info('[tor]: tor started');
}); });
@ -137,4 +143,15 @@ if (instances) {
control.listen(control_port, () => { control.listen(control_port, () => {
logger && logger.info(`[control]: Control Server listening on ${control_port}`); logger && logger.info(`[control]: Control Server listening on ${control_port}`);
}) })
function cleanUp(error) {
async.each(control.torPool.instances, (instance, next) => {
instance.exit(next);
}, (exitError) => {
process.exit(Number(Boolean(error || exitError)));
});
}
process.on('exit', cleanUp);
process.on('SIGINT', cleanUp);
process.on('uncaughtException', cleanUp);

91
package-lock.json generated
View file

@ -29,6 +29,19 @@
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
}, },
"array-union": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
"integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
"requires": {
"array-uniq": "1.0.3"
}
},
"array-uniq": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
"integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY="
},
"asn1": { "asn1": {
"version": "0.2.3", "version": "0.2.3",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
@ -314,6 +327,19 @@
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
}, },
"del": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz",
"integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=",
"requires": {
"globby": "6.1.0",
"is-path-cwd": "1.0.0",
"is-path-in-cwd": "1.0.1",
"p-map": "1.2.0",
"pify": "3.0.0",
"rimraf": "2.2.8"
}
},
"delayed-stream": { "delayed-stream": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@ -500,6 +526,25 @@
"path-is-absolute": "1.0.1" "path-is-absolute": "1.0.1"
} }
}, },
"globby": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
"integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
"requires": {
"array-union": "1.0.2",
"glob": "7.1.2",
"object-assign": "4.1.1",
"pify": "2.3.0",
"pinkie-promise": "2.0.1"
},
"dependencies": {
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
}
}
},
"graceful-readlink": { "graceful-readlink": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
@ -636,6 +681,27 @@
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
}, },
"is-path-cwd": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
"integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0="
},
"is-path-in-cwd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz",
"integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==",
"requires": {
"is-path-inside": "1.0.1"
}
},
"is-path-inside": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
"integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
"requires": {
"path-is-inside": "1.0.2"
}
},
"is-stream": { "is-stream": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
@ -986,6 +1052,11 @@
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true "dev": true
}, },
"nanoid": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-1.0.2.tgz",
"integrity": "sha512-sCTwJt690lduNHyqknXJp8pRwzm80neOLGaiTHU2KUJZFVSErl778NNCIivEQCX5gNT0xR1Jy3HEMe/TABT6lw=="
},
"native-dns": { "native-dns": {
"version": "git+https://github.com/znetstar/node-dns.git#336f1d3027b2a3da719b5cd65380219267901aeb", "version": "git+https://github.com/znetstar/node-dns.git#336f1d3027b2a3da719b5cd65380219267901aeb",
"requires": { "requires": {
@ -5493,6 +5564,11 @@
"integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=",
"dev": true "dev": true
}, },
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"once": { "once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -5542,6 +5618,11 @@
"p-limit": "1.2.0" "p-limit": "1.2.0"
} }
}, },
"p-map": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz",
"integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA=="
},
"p-try": { "p-try": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
@ -5557,6 +5638,11 @@
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
}, },
"path-is-inside": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
"integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM="
},
"path-key": { "path-key": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
@ -5568,6 +5654,11 @@
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
"dev": true "dev": true
}, },
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
},
"pinkie": { "pinkie": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",

View file

@ -21,10 +21,12 @@
}, },
"dependencies": { "dependencies": {
"async": "^2.1.4", "async": "^2.1.4",
"del": "^3.0.0",
"eventemitter2": "^3.0.0", "eventemitter2": "^3.0.0",
"get-port": "^2.1.0", "get-port": "^2.1.0",
"jrpc2": "^1.0.5", "jrpc2": "^1.0.5",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"nanoid": "^1.0.2",
"native-dns": "git+https://github.com/znetstar/node-dns.git", "native-dns": "git+https://github.com/znetstar/node-dns.git",
"nconf": "^0.10.0", "nconf": "^0.10.0",
"socks-proxy-agent": "^3.0.1", "socks-proxy-agent": "^3.0.1",

View file

@ -5,8 +5,8 @@ const HTTPServer = require('./HTTPServer');
const rpc = require('jrpc2'); const rpc = require('jrpc2');
class ControlServer { class ControlServer {
constructor(logger) { constructor(logger, nconf) {
this.torPool = new TorPool(null, null, logger); this.torPool = new TorPool(null, null, logger, nconf);
this.logger = logger; this.logger = logger;
let server = this.server = new rpc.Server(); let server = this.server = new rpc.Server();
@ -33,6 +33,15 @@ class ControlServer {
}); });
}).bind(this) ); }).bind(this) );
server.expose('addInstances', (function (instances) {
return new Promise((resolve, reject) => {
this.torPool.add(instances, (error, instances) => {
if (error) reject(error);
else resolve();
});
});
}).bind(this) );
server.expose('removeInstances', (function (instances) { server.expose('removeInstances', (function (instances) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.torPool.remove(instances, (error) => { this.torPool.remove(instances, (error) => {
@ -42,11 +51,25 @@ class ControlServer {
}); });
}).bind(this) ); }).bind(this) );
server.expose('removeInstancesAt', (function (instance_index) {
return new Promise((resolve, reject) => {
this.torPool.remove_at(instance_index, (error) => {
if (error) reject(error);
else resolve();
});
});
}).bind(this) );
server.expose('newIps', (function() { server.expose('newIps', (function() {
this.torPool.new_ips(); this.torPool.new_ips();
return Promise.resolve(); return Promise.resolve();
}).bind(this) ); }).bind(this) );
server.expose('newIpAt', (function(index) {
this.torPool.new_ip_at(index);
return Promise.resolve();
}).bind(this) );
server.expose('nextInstance', (function () { server.expose('nextInstance', (function () {
this.torPool.next(); this.torPool.next();
return Promise.resolve(); return Promise.resolve();
@ -70,13 +93,12 @@ class ControlServer {
} }
createTorPool(options) { createTorPool(options) {
this.torPool = new TorPool(null, options, this.logger); this.torPool = new TorPool(null, options, this.logger, this.nconf);
this.torPool;
return Promise.resolve(); return Promise.resolve();
} }
createSOCKSServer(port) { createSOCKSServer(port) {
this.socksServer = new SOCKSServer(this.torPool, this.logger); this.socksServer = new SOCKSServer(this.torPool, this.logger, this.nconf);
this.socksServer.listen(port || 9050); this.socksServer.listen(port || 9050);
this.logger && this.logger.info(`[socks]: Listening on ${port}`); this.logger && this.logger.info(`[socks]: Listening on ${port}`);
this.socksServer; this.socksServer;
@ -84,7 +106,7 @@ class ControlServer {
} }
createHTTPServer(port) { createHTTPServer(port) {
this.httpServer = new HTTPServer(this.torPool, this.logger); this.httpServer = new HTTPServer(this.torPool, this.logger, this.nconf);
this.httpServer.listen(port || 9080); this.httpServer.listen(port || 9080);
this.logger && this.logger.info(`[http]: Listening on ${port}`); this.logger && this.logger.info(`[http]: Listening on ${port}`);
this.httpServer; this.httpServer;
@ -92,7 +114,7 @@ class ControlServer {
} }
createDNSServer(port) { createDNSServer(port) {
this.dnsServer = new DNSServer(this.torPool, this.logger); this.dnsServer = new DNSServer(this.torPool, this.logger, this.nconf);
this.dnsServer.serve(port || 9053); this.dnsServer.serve(port || 9053);
this.logger && this.logger.info(`[dns]: Listening on ${port}`); this.logger && this.logger.info(`[dns]: Listening on ${port}`);
this.dnsServer; this.dnsServer;

View file

@ -2,8 +2,8 @@ const dns = require('native-dns');
const UDPServer = require('native-dns').UDPServer; const UDPServer = require('native-dns').UDPServer;
class DNSServer extends UDPServer { class DNSServer extends UDPServer {
constructor(tor_pool, logger, options, timeout) { constructor(tor_pool, logger, nconf) {
super(options || {}); super(this.nconf.get('dns:options'));
this.logger = logger; this.logger = logger;
this.tor_pool = tor_pool; this.tor_pool = tor_pool;
@ -14,7 +14,7 @@ class DNSServer extends UDPServer {
let outbound_req = dns.Request({ let outbound_req = dns.Request({
question, question,
server: { address: '127.0.0.1', port: dns_port, type: 'udp' }, server: { address: '127.0.0.1', port: dns_port, type: 'udp' },
timeout: this.timeout timeout: this.nconf.get('dns:timeout')
}); });
outbound_req.on('message', (err, answer) => { outbound_req.on('message', (err, answer) => {

View file

@ -6,7 +6,7 @@ const URL = require('url');
const SocksProxyAgent = require('socks-proxy-agent'); const SocksProxyAgent = require('socks-proxy-agent');
class HTTPServer extends Server { class HTTPServer extends Server {
constructor(tor_pool, logger) { constructor(tor_pool, logger, nconf) {
let handle_http_connections = (req, res) => { let handle_http_connections = (req, res) => {
let d = domain.create(); let d = domain.create();
d.on('error', (error) => { d.on('error', (error) => {
@ -154,6 +154,7 @@ class HTTPServer extends Server {
this.on('connect', handle_connect_connections); this.on('connect', handle_connect_connections);
this.logger = logger; this.logger = logger;
this.nconf = nconf;
this.tor_pool = tor_pool; this.tor_pool = tor_pool;
} }
}; };

View file

@ -3,7 +3,7 @@ const SOCKS5Server = socks.Server;
const domain = require('domain'); const domain = require('domain');
class SOCKSServer extends SOCKS5Server{ class SOCKSServer extends SOCKS5Server{
constructor(tor_pool, logger) { constructor(tor_pool, logger, nconf) {
let handleConnection = (info, accept, deny) => { let handleConnection = (info, accept, deny) => {
let d = domain.create(); let d = domain.create();
@ -79,6 +79,7 @@ class SOCKSServer extends SOCKS5Server{
super(handleConnection); super(handleConnection);
this.logger = logger; this.logger = logger;
this.nconf = nconf;
this.useAuth(socks.auth.None()); this.useAuth(socks.auth.None());
} }

View file

@ -20,30 +20,37 @@ Array.prototype.rotate = (function() {
const EventEmitter = require('eventemitter2').EventEmitter2; const EventEmitter = require('eventemitter2').EventEmitter2;
const async = require('async'); const async = require('async');
const TorProcess = require('./TorProcess'); const TorProcess = require('./TorProcess');
const temp = require('temp');
const _ = require('lodash'); const _ = require('lodash');
const path = require('path');
temp.track(); const nanoid = require('nanoid');
const fs = require('fs');
class TorPool extends EventEmitter { class TorPool extends EventEmitter {
constructor(tor_path, config, logger) { constructor(tor_path, default_config, logger, nconf) {
super(); super();
config = config || {}; default_config = _.extend({}, (default_config || {}), nconf.get('torConfig'));
this.default_tor_config = default_config;
this.tor_config = config; this.data_directory = nconf.get('parentDataDirectory');
!fs.existsSync(this.data_directory) && fs.mkdirSync(this.data_directory);
this.tor_path = tor_path || 'tor'; this.tor_path = tor_path || 'tor';
this._instances = []; this._instances = [];
this.logger = logger; this.logger = logger;
this.nconf = nconf;
} }
get instances() { get instances() {
return this._instances.filter((tor) => tor.ready).slice(0); return this._instances.filter((tor) => tor.ready).slice(0);
} }
create_instance(callback) { create_instance(instance_definition, callback) {
let config = _.extend({}, this.tor_config) instance_definition.Config = instance_definition.Config || {};
let instance = new TorProcess(this.tor_path, config, this.logger); instance_definition.Config = _.extend(instance_definition.Config, this.default_tor_config);
let instance_id = nanoid();
instance_definition.Config.DataDirectory = instance_definition.Config.DataDirectory || path.join(this.data_directory, instance_id);
let instance = new TorProcess(this.tor_path, instance_definition.Config, this.logger, this.nconf);
instance.id = instance_id;
instance.definition = instance_definition;
instance.create((error) => { instance.create((error) => {
if (error) return callback(error); if (error) return callback(error);
this._instances.push(instance); this._instances.push(instance);
@ -56,13 +63,23 @@ class TorPool extends EventEmitter {
}); });
} }
create(instances, callback) { add(instance_definitions, callback) {
if (!Number(instances)) return callback(null, []); async.each(instance_definitions, (instance_definition, next) => {
async.map(Array.from(Array(Number(instances))), (nothing, next) => { this.create_instance(instance_definition, next);
this.create_instance(next);
}, (callback || (() => {}))); }, (callback || (() => {})));
} }
create(instances, callback) {
if (typeof(instances) === 'number') {
instances = Array.from(Array(instances)).map(() => {
return {
Config: {}
};
});
}
return this.add(instances, callback);
}
remove(instances, callback) { remove(instances, callback) {
let instances_to_remove = this._instances.splice(0, instances); let instances_to_remove = this._instances.splice(0, instances);
async.each(instances_to_remove, (instance, next) => { async.each(instances_to_remove, (instance, next) => {
@ -70,18 +87,33 @@ class TorPool extends EventEmitter {
}, callback); }, callback);
} }
remove_at(instance_index, callback) {
let instance = this._instances.slice(instance_index, 1);
instance.exit(() => {
callback();
});
}
next() { next() {
this._instances = this._instances.rotate(1); this._instances = this._instances.rotate(1);
return this.instances[0]; return this.instances[0];
} }
exit() { exit(callback) {
this.instances.forEach((tor) => tor.exit()); async.each(this.instances, (instance,next) => {
instance.exit(next);
}, (error) => {
callback && callback(error);
});
} }
new_ips() { new_ips() {
this.instances.forEach((tor) => tor.new_ip()); this.instances.forEach((tor) => tor.new_ip());
} }
new_ip_at(index) {
this.instances[index].new_ip();
}
}; };
module.exports = TorPool; module.exports = TorPool;

View file

@ -1,33 +1,31 @@
const spawn = require('child_process').spawn; const spawn = require('child_process').spawn;
const _ = require('lodash'); const _ = require('lodash');
const temp = require('temp');
const async = require('async'); const async = require('async');
const fs = require('fs'); const fs = require('fs');
const getPort = require('get-port'); const getPort = require('get-port');
const del = require('del');
const EventEmitter = require('eventemitter2').EventEmitter2; const EventEmitter = require('eventemitter2').EventEmitter2;
const temp = require('temp');
temp.track(); temp.track();
class TorProcess extends EventEmitter { class TorProcess extends EventEmitter {
constructor(tor_path, config, logger) { constructor(tor_path, config, logger, nconf) {
super(); super();
this.tor_path = tor_path || 'tor'; this.tor_path = tor_path || 'tor';
this.nconf = nconf;
this.logger = logger; this.logger = logger;
this.tor_config = _.extend({ config.DataDirectory = config.DataDirectory || temp.mkdirSync();
Log: 'notice stdout',
DataDirectory: temp.mkdirSync(), this.tor_config = config;
NewCircuitPeriod: '10'
}, (config || { }));
} }
exit(callback) { exit(callback) {
this.process.once('exit', (code) => { this.process.once('exit', (code) => {
callback && callback(null, code); del(this.tor_config.DataDirectory).then(() => callback && callback(null, code)).catch((error) => callback && callback(error, code));
}); });
this.process.kill('SIGKILL'); this.process.kill('SIGKILL');
} }
new_ip() { new_ip() {