finishes documentation. minor bug fixes and some code clean-up
This commit is contained in:
parent
5b598f612a
commit
0b82ad9cb5
|
@ -7,4 +7,4 @@ docker-compose.yml
|
|||
README.md
|
||||
.vscode
|
||||
.DS_Store
|
||||
doc
|
||||
docs
|
|
@ -3,4 +3,4 @@ npm-debug.log
|
|||
.env
|
||||
.vscode
|
||||
.DS_Store
|
||||
doc
|
||||
docs
|
|
@ -2,4 +2,5 @@ node_modules
|
|||
npm-debug.log
|
||||
.env
|
||||
.vscode
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
docs
|
|
@ -3,7 +3,7 @@
|
|||
## [4.0.2] - 2018-09-13
|
||||
|
||||
### Added
|
||||
- Adds API documentation. To generate run `npm run doc` and open under `doc/index.html`
|
||||
- Adds API documentation. To generate run `npm run docs` and open under `docs/index.html`
|
||||
|
||||
### Changed
|
||||
- Much of the README has been moved to [the wiki](https://github.com/znetstar/tor-router/wiki)
|
||||
|
|
|
@ -26,11 +26,7 @@ ADD . /app
|
|||
|
||||
ENV HOME /home/tor_router
|
||||
|
||||
EXPOSE 9050
|
||||
|
||||
EXPOSE 9053
|
||||
|
||||
EXPOSE 9077
|
||||
EXPOSE 9050 9053 9077
|
||||
|
||||
ENTRYPOINT [ "tor-router" ]
|
||||
|
||||
|
|
|
@ -5,9 +5,8 @@ module.exports = function(grunt) {
|
|||
dist : {
|
||||
src: ['src/*.js', 'test/*.js', 'README.md'],
|
||||
options: {
|
||||
destination : 'doc',
|
||||
template : "node_modules/ink-docstrap/template",
|
||||
configure : "node_modules/ink-docstrap/template/jsdoc.conf.json"
|
||||
destination : 'docs',
|
||||
template : "node_modules/docdash"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,5 +16,5 @@ module.exports = function(grunt) {
|
|||
|
||||
grunt.registerTask('default', []);
|
||||
|
||||
grunt.registerTask('doc', ['jsdoc']);
|
||||
grunt.registerTask('docs', ['jsdoc']);
|
||||
};
|
|
@ -49,7 +49,7 @@ For example: `tor-router -j 3 -s 127.0.0.1:9050` would start the proxy with 3 to
|
|||
|
||||
For detailed examples and insturctions on using Tor Router [see the wiki](https://github.com/znetstar/tor-router/wiki).
|
||||
|
||||
To generate API documentation run `npm run doc`. The documentation will be available in `doc/`.
|
||||
To generate API documentation run `npm run docs`. The documentation will be available in `docs/`.
|
||||
|
||||
## Testing
|
||||
|
||||
|
|
|
@ -21,6 +21,15 @@
|
|||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
||||
"dev": true
|
||||
},
|
||||
"agent-base": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
|
||||
"integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"es6-promisify": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"ajv": {
|
||||
"version": "5.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
|
||||
|
@ -571,6 +580,12 @@
|
|||
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
|
||||
"dev": true
|
||||
},
|
||||
"docdash": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/docdash/-/docdash-1.0.0.tgz",
|
||||
"integrity": "sha512-HhK72PT4z55og8FDqskO/tTYXxU+LovRz+9pCDHLnUoPchkxjdIJidS+96LqW3CLrRdBmnkDRrcVrDFGLIluTw==",
|
||||
"dev": true
|
||||
},
|
||||
"dom-serializer": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz",
|
||||
|
@ -646,6 +661,21 @@
|
|||
"is-arrayish": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"es6-promise": {
|
||||
"version": "4.2.5",
|
||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz",
|
||||
"integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==",
|
||||
"dev": true
|
||||
},
|
||||
"es6-promisify": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
|
||||
"integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"es6-promise": "^4.0.3"
|
||||
}
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
|
@ -1195,16 +1225,6 @@
|
|||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
|
||||
},
|
||||
"ink-docstrap": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ink-docstrap/-/ink-docstrap-1.3.2.tgz",
|
||||
"integrity": "sha512-STx5orGQU1gfrkoI/fMU7lX6CSP7LBGO10gXNgOZhwKhUqbtNjCkYSewJtNnLmWP1tAGN6oyEpG1HFPw5vpa5Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"moment": "^2.14.1",
|
||||
"sanitize-html": "^1.13.0"
|
||||
}
|
||||
},
|
||||
"interpret": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",
|
||||
|
@ -1215,6 +1235,12 @@
|
|||
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
|
||||
"integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
|
||||
},
|
||||
"ip": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
|
||||
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
|
||||
"dev": true
|
||||
},
|
||||
"ipaddr.js": {
|
||||
"version": "0.1.9",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-0.1.9.tgz",
|
||||
|
@ -1475,36 +1501,6 @@
|
|||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
|
||||
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
|
||||
},
|
||||
"lodash.clonedeep": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.escaperegexp": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
|
||||
"integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.isstring": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
||||
"integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.mergewith": {
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz",
|
||||
"integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==",
|
||||
"dev": true
|
||||
},
|
||||
"logform": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/logform/-/logform-1.6.0.tgz",
|
||||
|
@ -1657,12 +1653,6 @@
|
|||
"supports-color": "5.4.0"
|
||||
}
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.22.2",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz",
|
||||
"integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=",
|
||||
"dev": true
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
|
@ -2005,17 +1995,6 @@
|
|||
"resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz",
|
||||
"integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE="
|
||||
},
|
||||
"postcss": {
|
||||
"version": "6.0.23",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
|
||||
"integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.4.1",
|
||||
"source-map": "^0.6.1",
|
||||
"supports-color": "^5.4.0"
|
||||
}
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
|
||||
|
@ -2172,6 +2151,25 @@
|
|||
"lodash": "^4.13.1"
|
||||
}
|
||||
},
|
||||
"requestretry": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/requestretry/-/requestretry-2.0.2.tgz",
|
||||
"integrity": "sha512-wBIylIEvvGHnFAYRXIKCARGzWxChn+mo7X3KjXPgtofB+c0ejcZFdZ5k6RFhBV+IOf80fkemcVuVdUKqovnj8A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"extend": "^3.0.2",
|
||||
"lodash": "^4.17.10",
|
||||
"when": "^3.7.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
|
@ -2221,24 +2219,6 @@
|
|||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||
"dev": true
|
||||
},
|
||||
"sanitize-html": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.19.0.tgz",
|
||||
"integrity": "sha512-Qt2imq49f2qP4537a7R2Xgx9sjTvw18jIT7zKurhu5kpYNQfMo8EZaW3OcpoXCvg3GTN4C4R3mN8ao7STUtKtA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.3.0",
|
||||
"htmlparser2": "^3.9.0",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lodash.escaperegexp": "^4.1.2",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.isstring": "^4.0.1",
|
||||
"lodash.mergewith": "^4.6.0",
|
||||
"postcss": "^6.0.14",
|
||||
"srcset": "^1.0.0",
|
||||
"xtend": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"secure-keys": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/secure-keys/-/secure-keys-1.0.0.tgz",
|
||||
|
@ -2282,6 +2262,12 @@
|
|||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
|
||||
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
|
||||
},
|
||||
"smart-buffer": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.1.tgz",
|
||||
"integrity": "sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg==",
|
||||
"dev": true
|
||||
},
|
||||
"sntp": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz",
|
||||
|
@ -2291,6 +2277,26 @@
|
|||
"hoek": "4.x.x"
|
||||
}
|
||||
},
|
||||
"socks": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/socks/-/socks-2.2.1.tgz",
|
||||
"integrity": "sha512-0GabKw7n9mI46vcNrVfs0o6XzWzjVa3h6GaSo2UPxtWAROXUWavfJWh1M4PR5tnE0dcnQXZIDFP4yrAysLze/w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ip": "^1.1.5",
|
||||
"smart-buffer": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"socks-proxy-agent": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz",
|
||||
"integrity": "sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"agent-base": "~4.2.0",
|
||||
"socks": "~2.2.0"
|
||||
}
|
||||
},
|
||||
"socksv5": {
|
||||
"version": "git+https://github.com/znetstar/socksv5.git#431e541390314adbbe765650d8d810ec1df38d8a",
|
||||
"from": "git+https://github.com/znetstar/socksv5.git#431e541390314adbbe765650d8d810ec1df38d8a",
|
||||
|
@ -2315,12 +2321,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
},
|
||||
"spdx-correct": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
|
||||
|
@ -2359,16 +2359,6 @@
|
|||
"integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw=",
|
||||
"dev": true
|
||||
},
|
||||
"srcset": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/srcset/-/srcset-1.0.0.tgz",
|
||||
"integrity": "sha1-pWad4StC87HV6D7QPHEEb8SPQe8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"array-uniq": "^1.0.2",
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz",
|
||||
|
@ -2586,6 +2576,12 @@
|
|||
"extsprintf": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"when": {
|
||||
"version": "3.7.8",
|
||||
"resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz",
|
||||
"integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I=",
|
||||
"dev": true
|
||||
},
|
||||
"which": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz",
|
||||
|
|
|
@ -13,17 +13,18 @@
|
|||
"start": "bin/tor-router -s -d -j 1",
|
||||
"test": "mocha -u tdd --exit test/index.js",
|
||||
"debug": "node --inspect-brk bin/tor-router",
|
||||
"build": "grunt",
|
||||
"doc": "grunt doc"
|
||||
"docs": "grunt docs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.1.2",
|
||||
"docdash": "^1.0.0",
|
||||
"grunt": "^1.0.3",
|
||||
"grunt-jsdoc": "^2.3.0",
|
||||
"ink-docstrap": "^1.3.2",
|
||||
"mocha": "^5.2.0",
|
||||
"request": "^2.79.0",
|
||||
"request-promise": "^4.2.2"
|
||||
"request-promise": "^4.2.2",
|
||||
"requestretry": "^2.0.2",
|
||||
"socks-proxy-agent": "^4.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"bluebird": "^3.5.2",
|
||||
|
|
|
@ -7,96 +7,266 @@ const DNSServer = require('./DNSServer');
|
|||
const TorPool = require('./TorPool');
|
||||
const default_ports = require('./default_ports');
|
||||
|
||||
/**
|
||||
* @typedef ControlServer~InstanceInfo
|
||||
*
|
||||
* @property {string} name - Name of the instance.
|
||||
* @property {string[]} group - Groups the instance belongs to.
|
||||
* @property {number} dns_port - Port Tor is listening on for DNS Traffic.
|
||||
* @property {number} socks_port - Port Tor is listening on for SOCKS Traffic.
|
||||
* @property {number} process_id - Process ID for the Tor process.
|
||||
* @property {Object} config - Configuration (torrc) set when starting the process.
|
||||
* @property {number} [weight] - Weight of the instance for weighted load balancing.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A server which exposes an RPC interface that can control the application.
|
||||
*/
|
||||
class ControlServer {
|
||||
constructor(logger, nconf) {
|
||||
this.torPool = new TorPool(nconf.get('torPath'), (() => nconf.get('torConfig')), nconf.get('parentDataDirectory'), nconf.get('loadBalanceMethod'), nconf.get('granaxOptions'), logger);
|
||||
this.logger = logger || require('./winston-silent-logger');
|
||||
/**
|
||||
*
|
||||
* @param {Provider} nconf - Instance of nconf.Provider that will be used to obtain configuration values.
|
||||
* @param {Logger} [logger] - Winston logger that will be used for logging. If not provided will disable logging.
|
||||
*/
|
||||
constructor(nconf, logger) {
|
||||
/**
|
||||
* Pool of Tor instances
|
||||
*
|
||||
* @type {TorPool}
|
||||
*/
|
||||
this.tor_pool = new TorPool(nconf.get('torPath'), (() => nconf.get('torConfig')), nconf.get('parentDataDirectory'), nconf.get('loadBalanceMethod'), nconf.get('granaxOptions'), logger);
|
||||
/**
|
||||
* Winston Logger for logging
|
||||
*
|
||||
* @type {Logger}
|
||||
*/
|
||||
this.logger = logger || require('./winston_silent_logger');
|
||||
/**
|
||||
* Nconf Provider for configuration
|
||||
*
|
||||
* @type {Provider}
|
||||
*/
|
||||
this.nconf = nconf;
|
||||
|
||||
/**
|
||||
* RPC Server instance
|
||||
*
|
||||
* @type {rpc.Server}
|
||||
*/
|
||||
let server = this.server = new rpc.Server();
|
||||
|
||||
/**
|
||||
* @borrows ControlServer#createTorPool as ControlServer~createTorPool
|
||||
* @function ControlServer~createTorPool
|
||||
*/
|
||||
server.expose('createTorPool', this.createTorPool.bind(this));
|
||||
/**
|
||||
* @borrows ControlServer#createSOCKSServer as ControlServer~createSOCKSServer
|
||||
* @function ControlServer~createSOCKSServer
|
||||
*/
|
||||
server.expose('createSOCKSServer', this.createSOCKSServer.bind(this));
|
||||
/**
|
||||
* @borrows ControlServer#createDNSServer as ControlServer~createDNSServer
|
||||
* @function ControlServer~createDNSServer
|
||||
*/
|
||||
server.expose('createDNSServer', this.createDNSServer.bind(this));
|
||||
/**
|
||||
* @borrows ControlServer#createHTTPServer as ControlServer~createHTTPServer
|
||||
* @function ControlServer~createHTTPServer
|
||||
*/
|
||||
server.expose('createHTTPServer', this.createHTTPServer.bind(this));
|
||||
|
||||
const instance_info = (i) => {
|
||||
return { group: i.instance_group, name: i.instance_name, dns_port: i.dns_port, socks_port: i.socks_port, process_id: i.process.pid, config: i.definition.Config, weight: i.definition.weight };
|
||||
};
|
||||
|
||||
|
||||
server.expose('queryInstances', (async () => {
|
||||
return this.torPool.instances.map(instance_info);
|
||||
/**
|
||||
* Returns a list of all instances currently in the pool.
|
||||
* @function ControlServer~queryInstances
|
||||
* @returns {ControlServer~InstanceInfo[]}
|
||||
*/
|
||||
server.expose('queryInstances', (() => {
|
||||
return this.tor_pool.instances.map(ControlServer.instance_info);
|
||||
}).bind(this));
|
||||
|
||||
server.expose('queryInstanceByName', (async (instance_name) => {
|
||||
let instance = this.torPool.instance_by_name(instance_name);
|
||||
/**
|
||||
* Returns information on an instance identified by the {@link TorProcess#instance_name} field.
|
||||
* @function ControlServer~queryInstanceByName
|
||||
* @param {string} instance_name - Name of the instance.
|
||||
* @returns {ControlServer~InstanceInfo}
|
||||
*/
|
||||
server.expose('queryInstanceByName', ((instance_name) => {
|
||||
let instance = this.tor_pool.instance_by_name(instance_name);
|
||||
|
||||
if (!instance)
|
||||
throw new Error(`Instance "${instance_name}"" does not exist`);
|
||||
|
||||
return instance_info(instance);
|
||||
return ControlServer.instance_info(instance);
|
||||
}).bind(this));
|
||||
|
||||
server.expose('queryInstanceAt', (async (index) => {
|
||||
if (!this.torPool)
|
||||
/**
|
||||
* Returns information on an instance identified by its index in the pool.
|
||||
* @function ControlServer~queryInstanceAt
|
||||
* @param {number} instance_index - Index of the instance in the pool.
|
||||
* @returns {ControlServer~InstanceInfo}
|
||||
*/
|
||||
server.expose('queryInstanceAt', ((index) => {
|
||||
if (!this.tor_pool)
|
||||
throw new Error('No pool created');
|
||||
|
||||
let instance = this.torPool.instance_at(index);
|
||||
let instance = this.tor_pool.instance_at(index);
|
||||
|
||||
if (!instance)
|
||||
throw new Error(`Instance at "${i}"" does not exist`);
|
||||
|
||||
return instance_info(this.torPool.instance_at(index));
|
||||
return ControlServer.instance_info(this.tor_pool.instance_at(index));
|
||||
}).bind(this));
|
||||
|
||||
server.expose('queryInstanceNames', (() => this.torPool.instance_names).bind(this));
|
||||
/**
|
||||
* Returns a list of the names of all of the instances in the pool.
|
||||
* @function ControlServer~queryInstanceNames
|
||||
* @returns {string[]}
|
||||
*/
|
||||
server.expose('queryInstanceNames', (() => this.tor_pool.instance_names).bind(this));
|
||||
|
||||
/**
|
||||
* Returns a list of the names of all of the current groups.
|
||||
* @function ControlServer~queryGroupNames
|
||||
* @returns {string[]}
|
||||
*/
|
||||
server.expose('queryGroupNames', (() => Array.from(this.tor_pool.group_names)).bind(this));
|
||||
|
||||
server.expose('queryGroupNames', (() => Array.from(this.torPool.group_names)).bind(this));
|
||||
/**
|
||||
* Returns a list of the instances that exist in a given group.
|
||||
* @function ControlServer~queryGroupNames
|
||||
* @param {string} group - Group the search in.
|
||||
* @returns {InstanceInfo[]}
|
||||
*/
|
||||
server.expose('queryInstancesByGroup', ((group) => this.tor_pool.instances_by_group(group).map(ControlServer.instance_info)).bind(this));
|
||||
|
||||
server.expose('queryInstancesByGroup', ((group) => this.torPool.instances_by_group(group).map(instance_info)).bind(this));
|
||||
/**
|
||||
* Creates instances from a number, an array of instance definitions or a single instance definition.
|
||||
* If a number is provided, creates n many instances.
|
||||
* @function ControlServer~createInstances
|
||||
* @param {InstanceDefinition[]|InstanceDefinition|number} instances_to_create - Array of definitions, single definition, or number of instances,
|
||||
* @async
|
||||
* @returns {Promise<InstanceInfo[]>} - The instances that were created
|
||||
*/
|
||||
server.expose('createInstances', (async (instances_to_create) => {
|
||||
let instances = await this.tor_pool.create(instances_to_create);
|
||||
|
||||
server.expose('createInstances', (async (num) => {
|
||||
let instances = await this.torPool.create(num);
|
||||
|
||||
return instances.map(instance_info);
|
||||
return instances.map(ControlServer.instance_info);
|
||||
}).bind(this));
|
||||
|
||||
/**
|
||||
* Creates instances from an array of instance definitions or a single instance definition.
|
||||
* @function ControlServer~addInstances
|
||||
* @param {InstanceDefinition[]|InstanceDefinition} instances_to_create - Array of definitions or single definition.
|
||||
* @async
|
||||
* @returns {Promise<InstanceInfo[]>} - The instances that were created
|
||||
*/
|
||||
server.expose('addInstances', (async (defs) => {
|
||||
let instances = await this.torPool.create(defs);
|
||||
let instances = await this.tor_pool.add(defs);
|
||||
|
||||
return instances.map(instance_info);
|
||||
return instances.map(ControlServer.instance_info);
|
||||
}).bind(this));
|
||||
|
||||
server.expose('removeInstances', this.torPool.remove.bind(this.torPool));
|
||||
/**
|
||||
* Removes a number of instances from the pool.
|
||||
* @function ControlServer~removeInstances
|
||||
* @borrows TorPool#remove as ControlServer~removeInstances
|
||||
*/
|
||||
server.expose('removeInstances', this.tor_pool.remove.bind(this.tor_pool));
|
||||
|
||||
server.expose('removeInstanceAt', this.torPool.remove_at.bind(this.torPool));
|
||||
/**
|
||||
* Remove an instance at the index provided from the pool.
|
||||
* @function ControlServer~removeInstanceAt
|
||||
* @borrows remove_at#remove as ControlServer~removeInstanceAt
|
||||
*/
|
||||
server.expose('removeInstanceAt', this.tor_pool.remove_at.bind(this.tor_pool));
|
||||
|
||||
server.expose('removeInstanceByName', this.torPool.remove_by_name.bind(this.torPool));
|
||||
/**
|
||||
* Remove an instance from the pool by the {@link TorProcess#instance_name} field.
|
||||
* @function ControlServer~removeInstanceByName
|
||||
* @borrows TorPool#remove_by_name as ControlServer~removeInstanceByName
|
||||
*/
|
||||
server.expose('removeInstanceByName', this.tor_pool.remove_by_name.bind(this.tor_pool));
|
||||
|
||||
server.expose('newIdentites', this.torPool.new_identites.bind(this.torPool));
|
||||
/**
|
||||
* Gets new identities for all instances in the pool.
|
||||
* @function ControlServer~newIdentites
|
||||
* @borrows TorPool#new_identites as ControlServer~newIdentites
|
||||
*/
|
||||
server.expose('newIdentites', this.tor_pool.new_identites.bind(this.tor_pool));
|
||||
|
||||
server.expose('newIdentityAt', this.torPool.new_identity_at.bind(this.torPool));
|
||||
/**
|
||||
* Get a new identity for the instance at the index provided in the pool.
|
||||
* @function ControlServer~newIdentityAt
|
||||
* @borrows TorPool#new_identity_at as ControlServer~newIdentityAt
|
||||
*/
|
||||
server.expose('newIdentityAt', this.tor_pool.new_identity_at.bind(this.tor_pool));
|
||||
|
||||
server.expose('newIdentityByName', this.torPool.new_identity_by_name.bind(this.torPool));
|
||||
/**
|
||||
* Get a new identity for the instance by the {@link TorProcess#instance_name} field.
|
||||
* @function ControlServer~newIdentityByName
|
||||
* @borrows TorPool#new_identity_by_name as ControlServer~newIdentityByName
|
||||
*/
|
||||
server.expose('newIdentityByName', this.tor_pool.new_identity_by_name.bind(this.tor_pool));
|
||||
|
||||
server.expose('newIdentitiesByGroup', (async (group) => await this.torPool.new_identites_by_group(group)).bind(this));
|
||||
/**
|
||||
* Gets new identities for all instances in the group.
|
||||
* @function ControlServer~newIdentitiesByGroup
|
||||
* @borrows TorPool#new_identites_by_group as ControlServer~newIdentitiesByGroup
|
||||
*/
|
||||
server.expose('newIdentitiesByGroup', this.tor_pool.new_identites_by_group.bind(this.tor_pool));
|
||||
|
||||
server.expose('nextInstance', (async () => instance_info( await this.torPool.next() )).bind(this));
|
||||
/**
|
||||
* Gets the next instance in the pool using the load balance method.
|
||||
* @function ControlServer~nextInstance
|
||||
* @returns {InstanceInfo} - The next instance in the pool.
|
||||
*/
|
||||
server.expose('nextInstance', (() => ControlServer.instance_info(this.tor_pool.next())).bind(this));
|
||||
|
||||
/**
|
||||
* Gets the next instance in the group using the load balance method.
|
||||
* @function ControlServer~nextInstanceByGroup
|
||||
* @param {string} group - The group in question.
|
||||
* @returns {InstanceInfo} - The next instance in the group.
|
||||
*/
|
||||
server.expose('nextInstanceByGroup', ((group) => {
|
||||
return instance_info(this.torPool.next_by_group(group));
|
||||
return ControlServer.instance_info(this.tor_pool.next_by_group(group));
|
||||
}).bind(this));
|
||||
|
||||
server.expose('closeInstances', (async () => this.torPool.exit()).bind(this));
|
||||
/**
|
||||
* Kills the processes of all instances in the pool.
|
||||
* @function ControlServer~closeInstances
|
||||
* @borrows TorPool#exit as ControlServer~closeInstances
|
||||
*/
|
||||
server.expose('closeInstances', this.tor_pool.exit.bind(this.tor_pool));
|
||||
|
||||
/**
|
||||
* Sets a property in the application configuration.
|
||||
* @function ControlServer~setConfig
|
||||
* @param {string} key - Name of the property to set.
|
||||
* @param {string} value - Value to set the property to.
|
||||
*/
|
||||
server.expose('setConfig', ((key, value) => {
|
||||
return this.nconf.set(key, value);
|
||||
this.nconf.set(key, value);
|
||||
}).bind(this));
|
||||
|
||||
/**
|
||||
* Gets a property in the application configuration.
|
||||
* @function ControlServer~getConfig
|
||||
* @param {string} key - Name of the property to set.
|
||||
* @returns {*} - Value of the property
|
||||
*/
|
||||
server.expose('getConfig', ((key) => {
|
||||
return this.nconf.get(key);
|
||||
}).bind(this));
|
||||
|
||||
/**
|
||||
* Saves the application configuration to the underlying store (usually a JSON file).
|
||||
* @function ControlServer~saveConfig
|
||||
* @async
|
||||
* @throws If the save operation fails.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
server.expose('saveConfig', (async () => {
|
||||
await new Promise((resolve, reject) => {
|
||||
this.nconf.save((err) => {
|
||||
|
@ -106,6 +276,13 @@ class ControlServer {
|
|||
});
|
||||
}).bind(this));
|
||||
|
||||
/**
|
||||
* Loads the application configuration from the underlying store (usually a JSON file).
|
||||
* @function ControlServer~loadConfig
|
||||
* @async
|
||||
* @throws If the load operation fails.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
server.expose('loadConfig', (async () => {
|
||||
await new Promise((resolve, reject) => {
|
||||
this.nconf.load((err) => {
|
||||
|
@ -115,107 +292,259 @@ class ControlServer {
|
|||
});
|
||||
}).bind(this));
|
||||
|
||||
server.expose('getDefaultTorConfig', (async () => {
|
||||
return this.nconf.get('torConfig');
|
||||
}).bind(this));
|
||||
|
||||
server.expose('setDefaultTorConfig', (async (config) => {
|
||||
this.nconf.set('torConfig', config);
|
||||
}).bind(this));
|
||||
|
||||
/**
|
||||
* Sets a configuration property on all instances in the pool.
|
||||
* @function ControlServer~setTorConfig
|
||||
* @async
|
||||
* @param {Object} config - An object containing properties to be set.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
server.expose('setTorConfig', (async (config) => {
|
||||
await Promise.all(Object.keys(config).map((key) => {
|
||||
let value = config[key];
|
||||
|
||||
return this.torPool.set_config_all(key, value);
|
||||
return this.tor_pool.set_config_all(key, value);
|
||||
}));
|
||||
}).bind(this));
|
||||
|
||||
/**
|
||||
* Sets a configuration property on all instances in a group.
|
||||
* @function ControlServer~setTorConfigByGroup
|
||||
* @async
|
||||
* @param {string} group - Group to set properties on.
|
||||
* @param {Object} config - An object containing properties to be set.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
server.expose('setTorConfigByGroup', (async (group, config) => {
|
||||
await Promise.all(Object.keys(config).map((key) => {
|
||||
let value = config[key];
|
||||
|
||||
return this.torPool.set_config_by_group(group, key, value);
|
||||
return this.tor_pool.set_config_by_group(group, key, value);
|
||||
}));
|
||||
}).bind(this));
|
||||
|
||||
server.expose('getLoadBalanceMethod', (async () => {
|
||||
return this.torPool.load_balance_method;
|
||||
/**
|
||||
* Retrieves the current load balance method for the pool
|
||||
* @function ControlServer~getLoadBalanceMethod
|
||||
* @returns {string} - The load balance method
|
||||
*/
|
||||
server.expose('getLoadBalanceMethod', (() => {
|
||||
return this.tor_pool.load_balance_method;
|
||||
}).bind(this));
|
||||
|
||||
/**
|
||||
* Sets the current load balance method for the pool
|
||||
* @function ControlServer~getLoadBalanceMethod
|
||||
* @param {string} load_balance_method - The load balance method to set
|
||||
*/
|
||||
server.expose('setLoadBalanceMethod', ((loadBalanceMethod) => {
|
||||
this.torPool.load_balance_method = loadBalanceMethod;
|
||||
this.tor_pool.load_balance_method = loadBalanceMethod;
|
||||
this.nconf.set('loadBalanceMethod', loadBalanceMethod);
|
||||
}).bind(this));
|
||||
|
||||
server.expose('getInstanceConfigByName', this.torPool.get_config_by_name.bind(this.torPool));
|
||||
/**
|
||||
* Retrieve a configuration property for an instance identified by the {@link TorProcess#instance_name} field.
|
||||
* @function ControlServer~getInstanceConfigByName
|
||||
* @borrows TorPool#getInstanceConfigByName as ControlServer~get_config_by_name
|
||||
*/
|
||||
server.expose('getInstanceConfigByName', this.tor_pool.get_config_by_name.bind(this.tor_pool));
|
||||
|
||||
server.expose('getInstanceConfigAt', this.torPool.get_config_at.bind(this.torPool));
|
||||
/**
|
||||
* Retrieves a configuration property for an instance by its index in the pool.
|
||||
* @function ControlServer~get_config_at
|
||||
* @borrows TorPool#get_config_at as ControlServer~get_config_at
|
||||
*/
|
||||
server.expose('getInstanceConfigAt', this.tor_pool.get_config_at.bind(this.tor_pool));
|
||||
|
||||
server.expose('setInstanceConfigByName', this.torPool.set_config_by_name.bind(this.torPool));
|
||||
/**
|
||||
* Sets a configuration property for an instance identified by the {@link TorProcess#instance_name} field.
|
||||
* @function ControlServer~setInstanceConfigByName
|
||||
* @borrows TorPool#set_config_by_name as ControlServer~setInstanceConfigByName
|
||||
*/
|
||||
server.expose('setInstanceConfigByName', this.tor_pool.set_config_by_name.bind(this.tor_pool));
|
||||
|
||||
server.expose('setInstanceConfigAt', this.torPool.set_config_at.bind(this.torPool));
|
||||
/**
|
||||
* Sets a configuration property for an instance identified by its index in the pool.
|
||||
* @function ControlServer~setInstanceConfigAt
|
||||
* @borrows TorPool#set_config_at as ControlServer~setInstanceConfigAt
|
||||
*/
|
||||
server.expose('setInstanceConfigAt', this.tor_pool.set_config_at.bind(this.tor_pool));
|
||||
|
||||
server.expose('signalAllInstances', this.torPool.signal_all.bind(this.torPool));
|
||||
/**
|
||||
* Sends a signal to all instances in the pool
|
||||
* @function ControlServer~signalAllInstances
|
||||
* @borrows TorPool#signal_all as ControlServer~signalAllInstances
|
||||
*/
|
||||
server.expose('signalAllInstances', this.tor_pool.signal_all.bind(this.tor_pool));
|
||||
|
||||
/**
|
||||
* Sends a signal to an instance identified by its index in the pool.
|
||||
* @function ControlServer~signalInstanceAt
|
||||
* @borrows TorPool#signal_at as ControlServer~signalInstanceAt
|
||||
*/
|
||||
server.expose('signalInstanceAt', this.tor_pool.signal_at.bind(this.tor_pool));
|
||||
|
||||
server.expose('signalInstanceAt', this.torPool.signal_at.bind(this.torPool));
|
||||
/**
|
||||
* Sends a signal to an instance identified by the {@link TorProcess#instance_name} field.
|
||||
* @function ControlServer~signalInstanceByName
|
||||
* @borrows TorPool#signal_by_name as ControlServer~signalInstanceByName
|
||||
*/
|
||||
server.expose('signalInstanceByName', this.tor_pool.signal_by_name.bind(this.tor_pool));
|
||||
|
||||
server.expose('signalInstanceByName', this.torPool.signal_by_name.bind(this.torPool));
|
||||
/**
|
||||
* Sends a singal to all instances in a group.
|
||||
* @function ControlServer~signalInstancesByGroup
|
||||
* @borrows TorPool#signal_by_group as ControlServer~signalInstancesByGroup
|
||||
*/
|
||||
server.expose('signalInstancesByGroup', this.tor_pool.signal_by_group.bind(this.tor_pool));
|
||||
|
||||
server.expose('signalInstancesByGroup', (async (group, signal) => await this.torPool.signal_by_group(group, signal)).bind(this));
|
||||
|
||||
server.expose('addInstanceToGroupByName', ((group, instance_name) => this.torPool.add_instance_to_group_by_name(group, instance_name)).bind(this));
|
||||
/**
|
||||
* Adds an instance to a group identified by its {@link TorProcess#instance_name} field.
|
||||
* @function ControlServer~addInstanceToGroupByName
|
||||
* @borrows TorPool#add_instance_to_group_by_name as ControlServer~addInstanceToGroupByName
|
||||
*/
|
||||
server.expose('addInstanceToGroupByName', this.tor_pool.add_instance_to_group_by_name.bind(this.tor_pool));
|
||||
|
||||
server.expose('addInstanceToGroupAt', ((group, instance_index) => this.torPool.add_instance_to_group_at(group, instance_index)).bind(this));
|
||||
/**
|
||||
* Adds an instance to a group identified by its index in the pool.
|
||||
* @function ControlServer~addInstanceToGroupAt
|
||||
* @borrows TorPool#add_instance_to_group_at as ControlServer~addInstanceToGroupAt
|
||||
*/
|
||||
server.expose('addInstanceToGroupAt', this.tor_pool.add_instance_to_group_at.bind(this.tor_pool));
|
||||
|
||||
server.expose('removeInstanceFromGroupByName', ((group, instance_name) => this.torPool.remove_instance_from_group_by_name(group, instance_name)).bind(this));
|
||||
/**
|
||||
* Removes an instance from a group identified by its {@link TorProcess#instance_name} field.
|
||||
* @function ControlServer~removeInstanceFromGroupByName
|
||||
* @borrows TorPool#remove_instance_from_group_by_name as ControlServer~removeInstanceFromGroupByName
|
||||
*/
|
||||
server.expose('removeInstanceFromGroupByName', this.tor_pool.remove_instance_from_group_by_name.bind(this.tor_pool));
|
||||
|
||||
server.expose('removeInstanceFromGroupAt', ((group, instance_index) => this.torPool.remove_instance_from_group_at(group, instance_index)).bind(this));
|
||||
/**
|
||||
* Remove an instance from a group identified by its index in the pool.
|
||||
* @function ControlServer~removeInstanceFromGroupAt
|
||||
* @borrows TorPool#remove_instance_from_group_at as ControlServer~removeInstanceFromGroupAt
|
||||
*/
|
||||
server.expose('removeInstanceFromGroupAt', this.tor_pool.remove_instance_from_group_at .bind(this.tor_pool));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a summary of information on the running instance
|
||||
* @param {TorProcess} instance
|
||||
* @static
|
||||
* @returns {ControlServer~InstanceInfo}
|
||||
*/
|
||||
static instance_info(instance) {
|
||||
return {
|
||||
name: instance.instance_name,
|
||||
group: instance.instance_group,
|
||||
dns_port: instance.dns_port,
|
||||
socks_port: instance.socks_port,
|
||||
process_id: instance.process.pid,
|
||||
config: instance.definition.Config,
|
||||
weight: instance.definition.weight
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the server to a host and port and begins listening for TCP traffic
|
||||
* @param {number} port - Port the server should bind to
|
||||
* @param {string} [hostname] - Host the server should bind to
|
||||
* @async
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async listenTcp(port, hostname) {
|
||||
this.tcpTransport = new rpc.tcpTransport({ port, hostname });
|
||||
this.tcpTransport.listen(this.server);
|
||||
this.logger.info(`[control]: control server listening on tcp://${hostname}:${port}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the server to a host and port and begins listening for WebSocket traffic
|
||||
* @param {number} port - Port the server should bind to
|
||||
* @param {string} [hostname] - Host the server should bind to
|
||||
* @async
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async listenWs(port, hostname) {
|
||||
this.wsTransport = new rpc.wsTransport({ port, hostname });
|
||||
this.wsTransport.listen(this.server);
|
||||
this.logger.info(`[control]: control server listening on ws://${hostname}:${port}`);
|
||||
}
|
||||
|
||||
async listen(port) { return await this.listenTcp(port); }
|
||||
/**
|
||||
* Calls {@link ControlServer#listenTcp} with the same arguments
|
||||
* @param {number} port - Port the server should bind to
|
||||
* @param {string} [hostname] - Host the server should bind to
|
||||
* @async
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async listen(port, hostname) { return await this.listenTcp(port, hostname); }
|
||||
|
||||
/**
|
||||
* Closes the TCP and/or WebSocket servers
|
||||
*/
|
||||
close() {
|
||||
return this.tcpTransport.tcpServer.close();
|
||||
if (this.tcpTransport && this.tcpTransport.tcpServer)
|
||||
this.tcpTransport.tcpServer.close();
|
||||
if (this.wsTransport && this.wsTransport.httpServer)
|
||||
this.wsTransport.httpServer.close();
|
||||
}
|
||||
|
||||
createTorPool(options) {
|
||||
this.torPool = new TorPool(this.nconf.get('torPath'), options, this.nconf.get('parentDataDirectory'), this.nconf.get('loadBalanceMethod'), this.nconf.get('granaxOptions'), this.logger);
|
||||
return this.torPool;
|
||||
/**
|
||||
* Creates a new {@link TorPool} instance
|
||||
* @param {Object} [tor_config] - Default Tor config to be used for the pool of instances.
|
||||
* @param {string} [load_balance_method] - Load balance method to be used for the pool. Will default to the global configuration if not provided.
|
||||
*
|
||||
* @returns {TorPool} - The {@link TorPool} that was created.
|
||||
*/
|
||||
createTorPool(tor_config, load_balance_method) {
|
||||
this.tor_pool = new TorPool(this.nconf.get('torPath'), tor_config, this.nconf.get('parentDataDirectory'), (load_balance_method || this.nconf.get('loadBalanceMethod')), this.nconf.get('granaxOptions'), this.logger);
|
||||
return this.tor_pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link SOCKSServer} and begins listening for traffic
|
||||
* @param {number} port - Port the server should bind to
|
||||
* @param {string} [hostname] - Host the server should bind to
|
||||
* @async
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async createSOCKSServer(port, hostname) {
|
||||
this.socksServer = new SOCKSServer(this.torPool, this.logger, (this.nconf.get('proxyByName') ? { mode: this.nconf.get('proxyByName'), deny_unidentified_users: this.nconf.get('denyUnidentifiedUsers') } : ""));
|
||||
this.socksServer = new SOCKSServer(this.tor_pool, this.logger, (this.nconf.get('proxyByName') ? { mode: this.nconf.get('proxyByName'), deny_unidentified_users: this.nconf.get('denyUnidentifiedUsers') } : ""));
|
||||
await this.socksServer.listen(port || default_ports.socks, hostname);
|
||||
this.logger.info(`[socks]: listening on socks5://${hostname}:${port}`);
|
||||
this.socksServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link HTTPServer} and begins listening for traffic
|
||||
* @param {number} port - Port the server should bind to
|
||||
* @param {string} [hostname] - Host the server should bind to
|
||||
* @async
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async createHTTPServer(port, hostname) {
|
||||
this.httpServer = new HTTPServer(this.torPool, this.logger, (this.nconf.get('proxyByName') ? { mode: this.nconf.get('proxyByName'), deny_unidentified_users: this.nconf.get('denyUnidentifiedUsers') } : ""));
|
||||
this.httpServer = new HTTPServer(this.tor_pool, this.logger, (this.nconf.get('proxyByName') ? { mode: this.nconf.get('proxyByName'), deny_unidentified_users: this.nconf.get('denyUnidentifiedUsers') } : ""));
|
||||
await this.httpServer.listen(port || default_ports.http, hostname);
|
||||
this.logger.info(`[http]: listening on http://${hostname}:${port}`);
|
||||
this.httpServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link DNSServer} and begins listening for traffic
|
||||
* @param {number} port - Port the server should bind to
|
||||
* @param {string} [hostname] - Host the server should bind to
|
||||
* @async
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async createDNSServer(port, hostname) {
|
||||
this.dnsServer = new DNSServer(this.torPool, this.nconf.get('dns:options'), this.nconf.get('dns:timeout'), this.logger);
|
||||
this.dnsServer = new DNSServer(this.tor_pool, this.nconf.get('dns:options'), this.nconf.get('dns:timeout'), this.logger);
|
||||
await this.dnsServer.serve(port || default_ports.dns, hostname);
|
||||
this.logger.info(`[dns]: listening on dns://${hostname}:${port}`);
|
||||
this.dnsServer;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Module that contains the {@link ControlServer} class.
|
||||
* @module tor-router/ControlServer
|
||||
* @see ControlServer
|
||||
*/
|
||||
module.exports = ControlServer;
|
||||
|
|
|
@ -1,8 +1,21 @@
|
|||
const dns = require('native-dns');
|
||||
const { UDPServer } = require('native-dns');
|
||||
const { UDPServer, Request } = dns;
|
||||
const Promise = require('bluebird');
|
||||
|
||||
/**
|
||||
* A DNS proxy server that will route requests to instances in the TorPool provided.
|
||||
* @extends UDPServer
|
||||
*/
|
||||
class DNSServer extends UDPServer {
|
||||
/**
|
||||
* Binds the server to a port and IP Address.
|
||||
*
|
||||
* @async
|
||||
* @param {number} port - The port to bind to.
|
||||
* @param {string} [host="::"] - Address to bind to. Will default to :: or 0.0.0.0 if not specified.
|
||||
* @returns {Promise}
|
||||
*
|
||||
*/
|
||||
async listen() {
|
||||
let args = Array.from(arguments);
|
||||
let inner_func = super.serve;
|
||||
|
@ -24,25 +37,32 @@ class DNSServer extends UDPServer {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of `DNSServer`.
|
||||
* @param {TorPool} tor_pool - The pool of instances that will be used for requests.
|
||||
* @param {Object} [dns_options] - Options that will be passed to the parent constructor.
|
||||
* @param {number} dns_timeout - How long to wait before each outbound DNS request before timing out.
|
||||
* @param {Logger} [logger] - Winston logger that will be used for logging. If not specified will disable logging.
|
||||
*/
|
||||
constructor(tor_pool, dns_options, dns_timeout, logger) {
|
||||
super(dns_options);
|
||||
this.logger = logger || require('./winston-silent-logger');
|
||||
this.tor_pool = tor_pool;
|
||||
|
||||
const handle_dns_request = (req, res) => {
|
||||
/**
|
||||
* Handles an incoming DNS request.
|
||||
*
|
||||
* @function handle_request
|
||||
* @param {Request} req - Incoming DNS request.
|
||||
* @param {Response} res - Outgoing DNS response.
|
||||
* @private
|
||||
*/
|
||||
const handle_request = (req, res) => {
|
||||
let connect = (tor_instance) => {
|
||||
let source = { hostname: req.address.address, port: req.address.port, proto: 'dns' };
|
||||
this.emit('instance-connection', tor_instance, source);
|
||||
for (let question of req.question) {
|
||||
let dns_port = (tor_instance.dns_port);
|
||||
let outbound_req = dns.Request({
|
||||
let outbound_req = Request({
|
||||
question,
|
||||
server: { address: '127.0.0.1', port: dns_port, type: 'udp' },
|
||||
timeout: dns_timeout
|
||||
timeout: this.dns_timeout
|
||||
});
|
||||
|
||||
this.logger.verbose(`[dns]: ${source.hostname}:${source.port} → 127.0.0.1:${dns_port}${tor_instance.definition.Name ? ' ('+tor_instance.definition.Name+')' : '' }`);
|
||||
|
||||
outbound_req.on('message', (err, answer) => {
|
||||
if (!err && answer) {
|
||||
for (let a of answer.answer){
|
||||
|
@ -52,11 +72,21 @@ class DNSServer extends UDPServer {
|
|||
});
|
||||
|
||||
outbound_req.on('error', (err) => {
|
||||
this.logger.error(`[dns]: an error occured while handling ar request: ${err.message}`);
|
||||
this.logger.error(`[dns]: an error occured while handling the request: ${err.message}`);
|
||||
});
|
||||
|
||||
|
||||
outbound_req.on('end', () => {
|
||||
let source = { hostname: req.address.address, port: req.address.port, proto: 'dns' };
|
||||
/**
|
||||
* Fires when the proxy has made a connection through an instance.
|
||||
*
|
||||
* @event DNSServer#instance-connection
|
||||
* @param {TorProcess} instance - Instance that has been connected to.
|
||||
* @param {InstanceConnectionSource} source - Details on the source of the connection.
|
||||
*/
|
||||
this.emit('instance_connection', tor_instance, source);
|
||||
this.logger.verbose(`[dns]: ${source.hostname}:${source.port} → 127.0.0.1:${dns_port}${tor_instance.definition.Name ? ' ('+tor_instance.definition.Name+')' : '' }`);
|
||||
res.send();
|
||||
});
|
||||
|
||||
|
@ -71,8 +101,36 @@ class DNSServer extends UDPServer {
|
|||
this.tor_pool.once('instance_created', connect);
|
||||
}
|
||||
};
|
||||
this.on('request', handle_dns_request);
|
||||
|
||||
super(dns_options);
|
||||
/**
|
||||
* Winston logger
|
||||
* @type {Logger}
|
||||
* @public
|
||||
*/
|
||||
this.logger = logger || require('./winston_silent_logger');
|
||||
|
||||
/**
|
||||
* The pool of instances that will be used for requests.
|
||||
* @type {TorPool}
|
||||
* @public
|
||||
*/
|
||||
this.tor_pool = tor_pool;
|
||||
|
||||
/**
|
||||
* Timeout for each outbound DNS request
|
||||
* @type {number}
|
||||
* @public
|
||||
*/
|
||||
this.dns_timeout = dns_timeout;
|
||||
|
||||
this.on('request', handle_request);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Module that contains the {@link DNSSErver} class.
|
||||
* @module tor-router/DNSServer
|
||||
* @see DNSServer
|
||||
*/
|
||||
module.exports = DNSServer;
|
|
@ -5,9 +5,36 @@ const { Server } = http;
|
|||
const Promise = require('bluebird');
|
||||
const socks = require('socksv5');
|
||||
|
||||
/**
|
||||
* Value of the "Proxy-Agent" header that will be sent with each http-connect (https) request
|
||||
* @constant
|
||||
* @type {string}
|
||||
* @default
|
||||
*/
|
||||
const TOR_ROUTER_PROXY_AGENT = 'tor-router';
|
||||
|
||||
/**
|
||||
* What will show up when an unauthenticated user attempts to connect when an invalid username
|
||||
* @constant
|
||||
* @type {string}
|
||||
* @default
|
||||
*/
|
||||
const REALM = 'Name of instance to route to';
|
||||
|
||||
/**
|
||||
* A HTTP(S) proxy server that will route requests to instances in the TorPool provided.
|
||||
* @extends Server
|
||||
*/
|
||||
class HTTPServer extends Server {
|
||||
/**
|
||||
* Binds the server to a port and IP Address.
|
||||
*
|
||||
* @async
|
||||
* @param {number} port - The port to bind to
|
||||
* @param {string} [host="::"] - Address to bind to. Will default to :: or 0.0.0.0 if not specified.
|
||||
* @returns {Promise}
|
||||
*
|
||||
*/
|
||||
async listen() {
|
||||
return await new Promise((resolve, reject) => {
|
||||
let args = Array.from(arguments);
|
||||
|
@ -20,21 +47,41 @@ class HTTPServer extends Server {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles username authentication for HTTP requests
|
||||
* @param {ClientRequest} req - Incoming HTTP request
|
||||
* @param {ClientResponse} res - Outgoing HTTP response
|
||||
* @private
|
||||
*/
|
||||
authenticate_user_http(req, res) {
|
||||
return this.authenticate_user(req, () => {
|
||||
res.writeHead(407, { 'Proxy-Authenticate': `Basic realm="Name of instance to route to"` });
|
||||
res.writeHead(407, { 'Proxy-Authenticate': `Basic realm="${REALM}"` });
|
||||
res.end();
|
||||
return false;
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles username authentication for HTTP-Connect requests
|
||||
* @param {ClientRequest} req - Incoming HTTP request
|
||||
* @param {Socket} socket - Inbound HTTP-Connect socket
|
||||
* @private
|
||||
*/
|
||||
authenticate_user_connect(req, socket) {
|
||||
return this.authenticate_user(req, () => {
|
||||
socket.write(`HTTP/1.1 407 Proxy Authentication Required\r\n'+'Proxy-Authenticate: Basic realm="${REALM}"\r\n` +'\r\n');
|
||||
socket.end();
|
||||
return false;
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the username provided against all groups (for "group" mode) or all instances (for "individual" mode).
|
||||
* @param {ClientRequest} req - Incoming HTTP request
|
||||
* @param {Function} deny - Function that when called will deny the connection to the proxy server and prompt the user for credentials (HTTP 407).
|
||||
* @private
|
||||
* @throws If the {@link HTTPServer#proxy_by_name} mode is invalid
|
||||
*/
|
||||
authenticate_user(req, deny) {
|
||||
if (!this.proxy_by_name)
|
||||
return true;
|
||||
|
@ -75,8 +122,21 @@ class HTTPServer extends Server {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of `HTTPServer`.
|
||||
* @param {TorPool} tor_pool - The pool of instances that will be used for requests.
|
||||
* @param {Logger} [logger] - Winston logger that will be used for logging. If not specified will disable logging.
|
||||
* @param {ProxyByNameConfig} [proxy_by_name] - Enable routing to specific instances or groups of instances using the username field (http://instance-1:@my-server:9050) when connecting.
|
||||
*/
|
||||
constructor(tor_pool, logger, proxy_by_name) {
|
||||
let handle_http_connections = (req, res) => {
|
||||
/**
|
||||
* Handles incoming HTTP Connections.
|
||||
* @function handle_http_connections
|
||||
* @param {ClientRequest} - Incoming HTTP request.
|
||||
* @param {ClientResponse} - Outgoing HTTP response.
|
||||
* @private
|
||||
*/
|
||||
const handle_http_connections = (req, res) => {
|
||||
if (!this.authenticate_user_http(req, res))
|
||||
return;
|
||||
|
||||
|
@ -103,10 +163,8 @@ class HTTPServer extends Server {
|
|||
|
||||
let connect = (tor_instance) => {
|
||||
let source = { hostname: req.connection.remoteAddress, port: req.connection.remotePort, proto: 'http', by_name: Boolean(instance) };
|
||||
this.emit('instance-connection', tor_instance, source);
|
||||
let socks_port = tor_instance.socks_port;
|
||||
this.logger.verbose(`[http-proxy]: ${source.hostname}:${source.port} → 127.0.0.1:${socks_port}${tor_instance.definition.Name ? ' ('+tor_instance.definition.Name+')' : '' } → ${url.hostname}:${url.port}`);
|
||||
|
||||
|
||||
let proxy_req = http.request({
|
||||
method: req.method,
|
||||
hostname: url.hostname,
|
||||
|
@ -120,6 +178,16 @@ class HTTPServer extends Server {
|
|||
localDNS: false
|
||||
})
|
||||
}, (proxy_res) => {
|
||||
/**
|
||||
* Fires when the proxy has made a connection through an instance using HTTP or HTTP-Connect.
|
||||
*
|
||||
* @event HTTPServer#instance-connection
|
||||
* @param {TorProcess} instance - Instance that has been connected to.
|
||||
* @param {InstanceConnectionSource} source - Details on the source of the connection.
|
||||
*/
|
||||
this.emit('instance_connection', tor_instance, source);
|
||||
this.logger.verbose(`[http-proxy]: ${source.hostname}:${source.port} → 127.0.0.1:${socks_port}${tor_instance.definition.Name ? ' ('+tor_instance.definition.Name+')' : '' } → ${url.hostname}:${url.port}`);
|
||||
|
||||
proxy_res.on('data', (chunk) => {
|
||||
res.write(chunk);
|
||||
});
|
||||
|
@ -165,9 +233,17 @@ class HTTPServer extends Server {
|
|||
this.logger.debug(`[http-proxy]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online`);
|
||||
tor_pool.once('instance_created', connect);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let handle_connect_connections = (req, inbound_socket, head) => {
|
||||
/**
|
||||
* Handles incoming HTTP-Connect connections.
|
||||
* @function handle_connect_connections
|
||||
* @param {ClientRequest} req - Incoming HTTP Request.
|
||||
* @param {Socket} inbound_socket - Incoming socket.
|
||||
* @param {Buffer|string} head - HTTP Request head.
|
||||
* @private
|
||||
*/
|
||||
const handle_connect_connections = (req, inbound_socket, head) => {
|
||||
if (!this.authenticate_user_connect(req, inbound_socket))
|
||||
return;
|
||||
|
||||
|
@ -178,9 +254,8 @@ class HTTPServer extends Server {
|
|||
|
||||
let connect = (tor_instance) => {
|
||||
let source = { hostname: req.connection.remoteAddress, port: req.connection.remotePort, proto: 'http-connect', by_name: Boolean(instance) };
|
||||
this.emit('instance-connection', tor_instance, source);
|
||||
|
||||
let socks_port = tor_instance.socks_port;
|
||||
this.logger && this.logger.verbose(`[http-connect]: ${source.hostname}:${source.port} → 127.0.0.1:${socks_port}${tor_instance.definition.Name ? ' ('+tor_instance.definition.Name+')' : '' } → ${hostname}:${port}`)
|
||||
var outbound_socket;
|
||||
|
||||
let onClose = (error) => {
|
||||
|
@ -204,6 +279,9 @@ class HTTPServer extends Server {
|
|||
localDNS: false,
|
||||
auths: [ socks.auth.None() ]
|
||||
}, ($outbound_socket) => {
|
||||
this.emit('instance_connection', tor_instance, source);
|
||||
this.logger && this.logger.verbose(`[http-connect]: ${source.hostname}:${source.port} → 127.0.0.1:${socks_port}${tor_instance.definition.Name ? ' ('+tor_instance.definition.Name+')' : '' } → ${hostname}:${port}`)
|
||||
|
||||
outbound_socket = $outbound_socket;
|
||||
outbound_socket.on('close', onClose);
|
||||
outbound_socket.on('error', onClose);
|
||||
|
@ -231,15 +309,34 @@ class HTTPServer extends Server {
|
|||
this.logger.debug(`[http-connect]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online`);
|
||||
this.tor_pool.once('instance_created', connect);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
super(handle_http_connections);
|
||||
this.on('connect', handle_connect_connections);
|
||||
|
||||
this.logger = logger || require('./winston-silent-logger');
|
||||
/**
|
||||
* Winston logger.
|
||||
* @type {Logger}
|
||||
* @public
|
||||
*/
|
||||
this.logger = logger || require('./winston_silent_logger');
|
||||
/**
|
||||
* The pool of instances that will be used for requests.
|
||||
* @type {TorPool}
|
||||
* @public
|
||||
*/
|
||||
this.tor_pool = tor_pool;
|
||||
/**
|
||||
* Configuration for "proxy by name" feature.
|
||||
* @type {ProxyByNameConfig}
|
||||
* @public
|
||||
*/
|
||||
this.proxy_by_name = proxy_by_name;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Module that contains the {@link HTTPServer} class.
|
||||
* @module tor-router/HTTPServer
|
||||
* @see HTTPServer
|
||||
*/
|
||||
module.exports = HTTPServer;
|
|
@ -2,7 +2,44 @@ const socks = require('socksv5');
|
|||
const Promise = require('bluebird');
|
||||
const { Server } = socks;
|
||||
|
||||
/**
|
||||
* Configuration for the "proxy by name" feature (connecting to specific instances or groups of instances using the username field when connecting).
|
||||
* @typedef ProxyByNameConfig
|
||||
*
|
||||
* @property {boolean} [deny_unidentified_users=false] - Deny unauthenticated (e.g. no username - socks://my-server:9050) users access to the proxy server.
|
||||
* @property {string} mode - Either "group" for routing to a group of instances or "individual" for routing to individual instances.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Details on the source of a connection the proxy server.
|
||||
* @typedef InstanceConnectionSource
|
||||
* @property {string} hostname - Hostname where the connection was made from.
|
||||
* @property {number} port - Port where the connection was made from.
|
||||
* @property {boolean} by_name - Indicates whether the connection was made using a username (made to a specific instance or group of instances).
|
||||
* @property {string} proto - The protocol of the connection "socks", "http", "http-connect" or "dns"
|
||||
*/
|
||||
|
||||
/**
|
||||
* A SOCKS5 proxy server that will route requests to instances in the TorPool provided.
|
||||
* @extends Server
|
||||
*/
|
||||
class SOCKSServer extends Server{
|
||||
/**
|
||||
* Callback for `authenticate_user`.
|
||||
* @callback SOCKSServer~authenticate_user_callback
|
||||
* @param {boolean} allow - Indicates if the connection should be allowed.
|
||||
* @param {boolean} user - Indicates if the connection should have a session (authentication was successful).
|
||||
*/
|
||||
|
||||
/**
|
||||
* Binds the server to a port and IP Address.
|
||||
*
|
||||
* @async
|
||||
* @param {number} port - The port to bind to.
|
||||
* @param {string} [host="::"] - Address to bind to. Will default to :: or 0.0.0.0 if not specified.
|
||||
* @returns {Promise}
|
||||
*
|
||||
*/
|
||||
async listen() {
|
||||
return await new Promise((resolve, reject) => {
|
||||
let args = Array.from(arguments);
|
||||
|
@ -15,6 +52,14 @@ class SOCKSServer extends Server{
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an instance from the pool or an instance from a group by the name provided.
|
||||
* @param {string} username - Name of the group or instance to route to.
|
||||
* @returns {TorProcess}
|
||||
* @throws If {@link SOCKSServer#proxy_by_name} is set to an invalid value.
|
||||
* @throws If the name of the instance or group provided is invalid.
|
||||
* @private
|
||||
*/
|
||||
get_instance_pbn(username) {
|
||||
if (this.proxy_by_name.mode === 'individual')
|
||||
return this.tor_pool.instance_by_name(username);
|
||||
|
@ -24,6 +69,14 @@ class SOCKSServer extends Server{
|
|||
throw Error(`Unknown "proxy_by_name" mode ${this.proxy_by_name.mode}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the username provided against all groups (for "group" mode) or all instances (for "individual" mode).
|
||||
* @param {string} username
|
||||
* @param {string} password
|
||||
* @param {SOCKSServer~authenticate_user_callback} callback - Callback for `authenticate_user`.
|
||||
* @throws If {@link SOCKSServer#proxy_by_name} is invalid.
|
||||
* @private
|
||||
*/
|
||||
authenticate_user(username, password, callback) {
|
||||
let deny_un = this.proxy_by_name.deny_unidentified_users;
|
||||
|
||||
|
@ -45,8 +98,23 @@ class SOCKSServer extends Server{
|
|||
callback(true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of `SOCKSServer`.
|
||||
* @param {TorPool} tor_pool - The pool of instances that will be used for requests
|
||||
* @param {Logger} [logger] - Winston logger that will be used for logging. If not specified will disable logging.
|
||||
* @param {ProxyByNameConfig} [proxy_by_name] - Enable routing to specific instances or groups of instances using the username field (socks://instance-1:@my-server:9050) when connecting.
|
||||
*/
|
||||
constructor(tor_pool, logger, proxy_by_name) {
|
||||
let handleConnection = (info, accept, deny) => {
|
||||
/**
|
||||
* Handles SOCKS5 inbound connections.
|
||||
*
|
||||
* @function handle_connections
|
||||
* @param {object} info - Information about the inbound connection.
|
||||
* @param {Function} accept - Callback that allows the connection.
|
||||
* @param {Function} deny - Callback that denies the connection.
|
||||
* @private
|
||||
*/
|
||||
const handle_connections = (info, accept, deny) => {
|
||||
let inbound_socket = accept(true);
|
||||
let instance;
|
||||
|
||||
|
@ -77,8 +145,6 @@ class SOCKSServer extends Server{
|
|||
let connect = (tor_instance) => {
|
||||
let source = { hostname: info.srcAddr, port: info.srcPort, proto: 'socks', by_name: Boolean(instance) };
|
||||
let socks_port = tor_instance.socks_port;
|
||||
this.emit('instance-connection', tor_instance, source);
|
||||
this.logger.verbose(`[socks]: ${source.hostname}:${source.port} → 127.0.0.1:${socks_port}${tor_instance.definition.Name ? ' ('+tor_instance.definition.Name+')' : '' } → ${info.dstAddr}:${info.dstPort}`)
|
||||
|
||||
socks.connect({
|
||||
host: info.dstAddr,
|
||||
|
@ -88,6 +154,16 @@ class SOCKSServer extends Server{
|
|||
localDNS: false,
|
||||
auths: [ socks.auth.None() ]
|
||||
}, ($outbound_socket) => {
|
||||
/**
|
||||
* Fires when the proxy has made a connection through an instance.
|
||||
*
|
||||
* @event SOCKSServer#instance-connection
|
||||
* @param {TorProcess} instance - Instance that has been connected to.
|
||||
* @param {InstanceConnectionSource} source - Details on the source of the connection.
|
||||
*/
|
||||
this.emit('instance_connection', tor_instance, source);
|
||||
this.logger.verbose(`[socks]: ${source.hostname}:${source.port} → 127.0.0.1:${socks_port}${tor_instance.definition.Name ? ' ('+tor_instance.definition.Name+')' : '' } → ${info.dstAddr}:${info.dstPort}`)
|
||||
|
||||
outbound_socket = $outbound_socket;
|
||||
outbound_socket && outbound_socket.on('close', onClose);
|
||||
|
||||
|
@ -123,9 +199,8 @@ class SOCKSServer extends Server{
|
|||
this.logger.debug(`[socks]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online`);
|
||||
this.tor_pool.once('instance_created', connect);
|
||||
}
|
||||
};
|
||||
|
||||
super(handleConnection);
|
||||
}
|
||||
super(handle_connections);
|
||||
|
||||
let auth = socks.auth.None();
|
||||
|
||||
|
@ -135,11 +210,34 @@ class SOCKSServer extends Server{
|
|||
|
||||
this.useAuth(auth);
|
||||
|
||||
this.logger = logger || require('./winston-silent-logger');
|
||||
/**
|
||||
* Winston logger to use.
|
||||
*
|
||||
* @type {Logger}
|
||||
* @public
|
||||
*/
|
||||
this.logger = logger || require('./winston_silent_logger');
|
||||
/**
|
||||
* Pool of instances use to service requests.
|
||||
*
|
||||
* @type {TorPool}
|
||||
* @public
|
||||
*/
|
||||
this.tor_pool = tor_pool;
|
||||
/**
|
||||
* Configuration for the "proxy by name" feature.
|
||||
*
|
||||
* @type {ProxyByNameConfig}
|
||||
* @public
|
||||
*/
|
||||
this.proxy_by_name = proxy_by_name;
|
||||
this.logger.debug(`[socks]: connecting to a specific instance by name has ben turned ${proxy_by_name ? 'on' : 'off'}`);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Module that contains the {@link SOCKSServer} class.
|
||||
* @module tor-router/SOCKSServer
|
||||
* @see SOCKSServer
|
||||
*/
|
||||
module.exports = SOCKSServer;
|
|
@ -1,4 +1,6 @@
|
|||
/* From http://stackoverflow.com/questions/1985260/javascript-array-rotate */
|
||||
/**
|
||||
* @author Christoph and Marco Bonelli on Stackoverflow <https://bit.ly/2p6gedO>
|
||||
*/
|
||||
Array.prototype.rotate = (function() {
|
||||
// save references to array functions to make lookup faster
|
||||
var push = Array.prototype.push,
|
||||
|
@ -29,21 +31,19 @@ const TorProcess = require('./TorProcess');
|
|||
|
||||
Promise.promisifyAll(fs);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class that represents a pool of Tor processes.
|
||||
* @extends EventEmitter
|
||||
*/
|
||||
class TorPool extends EventEmitter {
|
||||
/**
|
||||
* Creates an instance of TorPool.
|
||||
* Creates an instance of `TorPool`.
|
||||
*
|
||||
* @param {string} tor_path - Path to the Tor executable.
|
||||
* @param {Object|Function} [default_config] - Default configuration that will be passed to all Tor instances created. Can be a function. See {@link https://bit.ly/2QrmI3o|Tor Documentation} for all possible options
|
||||
* @param {string} data_directory - Parent directory for the data directory of each proccess.
|
||||
* @param {string} load_balance_method - Name of the load balance method to use. See {@link TorPool#load_balance_methods}.
|
||||
* @param {string} [granax_options] - Object containing options that will be passed to granax.
|
||||
* @param {string} [granax_options] - Object containing options that will be passed to granax for each instance.
|
||||
* @param {string} [logger] - A winston logger. If not provided no logging will occur.
|
||||
*
|
||||
* @throws If "data_directory" is not provided.
|
||||
|
@ -54,10 +54,40 @@ class TorPool extends EventEmitter {
|
|||
super();
|
||||
this._instances = [];
|
||||
this._default_tor_config = default_config;
|
||||
/**
|
||||
* Parent directory for the data directory of each proccess.
|
||||
*
|
||||
* @type {string}
|
||||
* @public
|
||||
*/
|
||||
this.data_directory = data_directory;
|
||||
/**
|
||||
* Name of the load balance method to use.
|
||||
*
|
||||
* @type {string}
|
||||
* @public
|
||||
*/
|
||||
this.load_balance_method = load_balance_method;
|
||||
/**
|
||||
* Path to the Tor executable.
|
||||
*
|
||||
* @type {string}
|
||||
* @public
|
||||
*/
|
||||
this.tor_path = tor_path;
|
||||
this.logger = logger || require('./winston-silent-logger');
|
||||
/**
|
||||
* The winston logger.
|
||||
*
|
||||
* @type {Logger}
|
||||
* @public
|
||||
*/
|
||||
this.logger = logger || require('./winston_silent_logger');
|
||||
/**
|
||||
* Object containing options that will be passed to granax for each instance.
|
||||
*
|
||||
* @type {Logger}
|
||||
* @public
|
||||
*/
|
||||
this.granax_options = granax_options;
|
||||
}
|
||||
|
||||
|
@ -182,7 +212,7 @@ class TorPool extends EventEmitter {
|
|||
* Represents a group of instances. Group is a Proxy with an array as its object. The array is generated by calling {@link TorPool#instances_in_group}.
|
||||
* When called with an index (e.g. `Group[0]`) will return the instance at that index.
|
||||
* Helper functions are available as properties.
|
||||
* @typedef {TorProcess[]} Group
|
||||
* @typedef {TorProcess[]} InstanceGroup
|
||||
*
|
||||
* @property {Function} add - Adds an instance to the group.
|
||||
* @property {Function} remove - Removes an instance from the group.
|
||||
|
@ -196,14 +226,14 @@ class TorPool extends EventEmitter {
|
|||
/**
|
||||
* Represents a collection of groups as an associative array. GroupCollection is a Proxy with a Set as its object. The Set is {@link TorPool#group_names}.
|
||||
* If a non-existant group is referenced (e.g. `Groups["doesn't exist"]`) it will be created. So `Groups["doesn't exist"].add(my_instance)` will create the group and add the instance to it.
|
||||
* @typedef {Group[]} GroupCollection
|
||||
* @typedef {InstanceGroup[]} InstanceGroupCollection
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents all groups currently in the pool.
|
||||
*
|
||||
* @readonly
|
||||
* @type {GroupCollection}
|
||||
* @type {InstanceGroupCollection}
|
||||
*/
|
||||
get groups() {
|
||||
let groupHandler = {
|
||||
|
@ -745,4 +775,9 @@ class TorPool extends EventEmitter {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Module that contains the {@link TorPool} class.
|
||||
* @module tor-router/TorPool
|
||||
* @see TorPool
|
||||
*/
|
||||
module.exports = TorPool;
|
|
@ -5,7 +5,7 @@ const crypto = require('crypto');
|
|||
const { connect } = require('net');
|
||||
const os = require('os');
|
||||
|
||||
const Promise = require('bluebird');
|
||||
const Promise = require('bluebird');
|
||||
const _ = require('lodash');
|
||||
const { EventEmitter } = require('eventemitter3');
|
||||
const shell = require('shelljs');
|
||||
|
@ -36,7 +36,7 @@ temp.track();
|
|||
*/
|
||||
class TorProcess extends EventEmitter {
|
||||
/**
|
||||
* Creates an instance of TorProcess
|
||||
* Creates an instance of `TorProcess`
|
||||
*
|
||||
* @param {string} tor_path - Path to the Tor executable.
|
||||
* @param {InstanceDefinition} [definition] - Object containing various options for the instance. See {@link InstanceDefinition} for more info.
|
||||
|
@ -45,7 +45,7 @@ class TorProcess extends EventEmitter {
|
|||
*/
|
||||
constructor(tor_path, definition, granax_options, logger) {
|
||||
super();
|
||||
this.logger = logger || require('./winston-silent-logger');
|
||||
this.logger = logger || require('./winston_silent_logger');
|
||||
|
||||
definition = definition || {};
|
||||
definition.Group = definition.Group ? [].concat(definition.Group) : [];
|
||||
|
@ -305,7 +305,6 @@ class TorProcess extends EventEmitter {
|
|||
|
||||
tor.stderr.on('data', (data) => {
|
||||
let error_message = Buffer.from(data).toString('utf8');
|
||||
|
||||
this.emit('error', new Error(error_message));
|
||||
});
|
||||
|
||||
|
@ -315,29 +314,42 @@ class TorProcess extends EventEmitter {
|
|||
this.logger.info(`[tor-${this.instance_name}]: tor is ready`);
|
||||
});
|
||||
|
||||
this.on('control_listen', () => {
|
||||
this._controller = new TorController(connect(this._control_port), _.extend({ authOnConnect: false }, this.granax_options));
|
||||
Promise.promisifyAll(this._controller);
|
||||
|
||||
this.controller.on('ready', () => {
|
||||
this.logger.debug(`[tor-${this.instance_name}]: connected to tor control port`);
|
||||
this.controller.authenticate(`"${this.control_password}"`, (err) => {
|
||||
if (err) {
|
||||
this.logger.error(`[tor-${this.instance_name}]: ${err.stack}`);
|
||||
this.emit('error', err);
|
||||
} else {
|
||||
this.logger.debug(`[tor-${this.instance_name}]: authenticated with tor instance via the control port`);
|
||||
this.control_port_connected = true;
|
||||
/**
|
||||
* An event that fires when a connection has been established to the control protocol.
|
||||
*
|
||||
* @event TorProcess#controller_ready
|
||||
*/
|
||||
this.emit('controller_ready');
|
||||
}
|
||||
this.on('control_listen', (async () => {
|
||||
try {
|
||||
let socket = connect(this.control_port);
|
||||
socket.on('error', ((error) => {
|
||||
this.logger.error(`[tor-${this.instance_name}]: ${error.stack}`);
|
||||
this.emit('error', error);
|
||||
}));
|
||||
this._controller = new TorController(socket, _.extend({ authOnConnect: false }, this.granax_options));
|
||||
Promise.promisifyAll(this._controller);
|
||||
this.controller.on('ready', () => {
|
||||
this.logger.debug(`[tor-${this.instance_name}]: connected via the tor control protocol`);
|
||||
this.controller.authenticate(`"${this.control_password}"`, (err) => {
|
||||
if (err) {
|
||||
this.logger.error(`[tor-${this.instance_name}]: ${err.stack}`);
|
||||
this.emit('error', err);
|
||||
} else {
|
||||
this.logger.debug(`[tor-${this.instance_name}]: authenticated with tor instance via the control protocol`);
|
||||
/**
|
||||
* Is true when the connected via the control protcol
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.control_port_connected = true;
|
||||
/**
|
||||
* An event that fires when a connection has been established to the control protocol.
|
||||
*
|
||||
* @event TorProcess#controller_ready
|
||||
*/
|
||||
this.emit('controller_ready');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error(`[tor-${this.instance_name}]: ${err.stack}`);
|
||||
this.emit('error', err);
|
||||
}
|
||||
}));
|
||||
|
||||
tor.stdout.on('data', (data) => {
|
||||
let text = Buffer.from(data).toString('utf8');
|
||||
|
@ -373,18 +385,18 @@ class TorProcess extends EventEmitter {
|
|||
}
|
||||
|
||||
if (text.indexOf('Opening DNS listener on') !== -1) {
|
||||
this.dns_port_listening = true;
|
||||
/**
|
||||
* An event that fires when the Tor process has started listening for DNS traffic.
|
||||
*
|
||||
* @event TorProcess#dns_listen
|
||||
*/
|
||||
this.dns_port_listening = true;
|
||||
this.emit('dns_listen');
|
||||
}
|
||||
|
||||
if (text.indexOf('[err]') !== -1) {
|
||||
/**
|
||||
* An event that fires the Tor process has written an error to stdout or stderr or an error occured connecting to the control protocol.
|
||||
* An event that fires when the Tor process has written an error to stdout, stderr or when an error occurs connecting via the control protocol.
|
||||
*
|
||||
* @event TorProcess#error
|
||||
* @type {Error}
|
||||
|
@ -409,4 +421,9 @@ class TorProcess extends EventEmitter {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Module that contains the {@link TorProcess} class.
|
||||
* @module tor-router/TorProcess
|
||||
* @see TorProcess
|
||||
*/
|
||||
module.exports = TorProcess;
|
|
@ -1,7 +1,11 @@
|
|||
// Default configuration for Tor Router
|
||||
const temp = require('temp');
|
||||
const path = require('path');
|
||||
temp.track();
|
||||
|
||||
/**
|
||||
* This module cotains the default configuration for the application.
|
||||
* @module tor-router/default_config
|
||||
*/
|
||||
module.exports = {
|
||||
"controlHost": 9077,
|
||||
"websocketControlHost": null,
|
||||
|
@ -20,7 +24,9 @@ module.exports = {
|
|||
"torPath": (() => {
|
||||
let platform = require('os').platform();
|
||||
let BIN_PATH = path.join(__dirname, '..', 'node_modules', 'granax', 'bin');
|
||||
/* Taken from https://github.com/bookchin/granax/blob/master/index.js */
|
||||
/**
|
||||
* @author gordonhall on GitLab <https://bit.ly/2xcahjY>
|
||||
*/
|
||||
switch (platform) {
|
||||
case 'win32':
|
||||
return path.join(BIN_PATH, 'Browser', 'TorBrowser', 'Tor', 'tor.exe');
|
||||
|
@ -39,8 +45,8 @@ module.exports = {
|
|||
})(),
|
||||
"instances": null,
|
||||
"dns": {
|
||||
"options": {},
|
||||
"timeout": null
|
||||
"timeout": 10000,
|
||||
"options": {}
|
||||
},
|
||||
"granaxOptions": null
|
||||
};
|
|
@ -1,3 +1,7 @@
|
|||
/**
|
||||
* This module cotains the default ports the application will bind to.
|
||||
* @module tor-router/default_ports
|
||||
*/
|
||||
module.exports = Object.freeze({
|
||||
socks: 9050,
|
||||
http: 9080,
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
/**
|
||||
* Index module for the project
|
||||
* @module tor-router
|
||||
*/
|
||||
module.exports = {
|
||||
TorProcess: require('./TorProcess'),
|
||||
TorPool: require('./TorPool'),
|
||||
DNSServer: require('./DNSServer'),
|
||||
SOCKSServer: require('./SOCKSServer'),
|
||||
HTTPServer: require('./HTTPServer'),
|
||||
DNSServer: require('./DNSServer'),
|
||||
ControlServer: require('./ControlServer')
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
const fs = require('fs');
|
||||
|
||||
const nconf = require('nconf');
|
||||
const { Provider } = require('nconf');
|
||||
const yargs = require('yargs');
|
||||
const winston = require('winston')
|
||||
const Promise = require('bluebird');
|
||||
|
@ -10,20 +10,46 @@ const default_ports = require('./default_ports');
|
|||
|
||||
const package_json = JSON.parse(fs.readFileSync(`${__dirname}/../package.json`, 'utf8'));
|
||||
|
||||
/**
|
||||
* @typedef Host
|
||||
* @property {string} hostname - The hostname
|
||||
* @property {number} port - The port
|
||||
* @private
|
||||
*/
|
||||
|
||||
/**
|
||||
* Extracts the host and port components from a string
|
||||
* @param {string} host
|
||||
* @returns {Host}
|
||||
* @private
|
||||
*/
|
||||
function extractHost (host) {
|
||||
if (typeof(host) === 'number')
|
||||
return { hostname: (typeof(default_ports.default_host) === 'string' ? default_ports.default_host : '0.0.0.0'), port: host };
|
||||
return { hostname: (typeof(default_ports.default_host) === 'string' ? default_ports.default_host : ''), port: host };
|
||||
else if (typeof(host) === 'string' && host.indexOf(':') !== -1)
|
||||
return { hostname: host.split(':').shift(), port: Number(host.split(':').pop()) };
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an object with a hostname and port returns a formatted string
|
||||
* @param {Host} host
|
||||
* @returns {string} - Formatted host (e.g. "0.0.0.0:1234")
|
||||
*/
|
||||
function assembleHost(host) {
|
||||
return `${typeof(host.hostname) === 'string' ? host.hostname : '' }:${host.port}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function for the application
|
||||
* @param {Provider} nconf - Instance of `nconf.Provider` used for configuration.
|
||||
* @param {Logger} [logger] - Winston logger to be used for logging. If not provided will disable logging.
|
||||
* @async
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function main(nconf, logger) {
|
||||
Promise.promisifyAll(nconf);
|
||||
let instances = nconf.get('instances');
|
||||
let socks_host = typeof(nconf.get('socksHost')) !== 'boolean' ? extractHost(nconf.get('socksHost')) : nconf.get('socksHost');
|
||||
let dns_host = typeof(nconf.get('dnsHost')) !== 'boolean' ? extractHost(nconf.get('dnsHost')) : nconf.get('dnsHost');
|
||||
|
@ -44,7 +70,7 @@ async function main(nconf, logger) {
|
|||
nconf.set('websocketControlPort', assembleHost(control_host_ws));
|
||||
}
|
||||
|
||||
let control = new ControlServer(logger, nconf);
|
||||
let control = new ControlServer(nconf, logger);
|
||||
|
||||
try {
|
||||
await control.listenTcp(control_host.port, control_host.hostname);
|
||||
|
@ -80,7 +106,7 @@ async function main(nconf, logger) {
|
|||
if (instances) {
|
||||
logger.info(`[tor]: starting ${Array.isArray(instances) ? instances.length : instances} tor instance(s)...`);
|
||||
|
||||
await control.torPool.create(instances);
|
||||
await control.tor_pool.create(instances);
|
||||
|
||||
logger.info('[tor]: tor started');
|
||||
}
|
||||
|
@ -89,11 +115,17 @@ async function main(nconf, logger) {
|
|||
process.exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kills all tor processes and exits, logging an error if one occurs.
|
||||
* @function cleanUp
|
||||
*
|
||||
* @param {Error} error - Error or exit code
|
||||
*/
|
||||
const cleanUp = (async (error) => {
|
||||
let thereWasAnExitError = false;
|
||||
let { handleError } = this;
|
||||
try {
|
||||
await control.torPool.exit();
|
||||
await control.tor_pool.exit();
|
||||
} catch (exitError) {
|
||||
logger.error(`[global]: error closing tor instances: ${exitError.message}`);
|
||||
thereWasAnExitError = true;
|
||||
|
@ -111,7 +143,7 @@ async function main(nconf, logger) {
|
|||
process.title = 'tor-router';
|
||||
|
||||
process.on('SIGHUP', () => {
|
||||
control.torPool.new_identites();
|
||||
control.tor_pool.new_identites();
|
||||
});
|
||||
|
||||
process.on('exit', cleanUp);
|
||||
|
@ -119,6 +151,12 @@ async function main(nconf, logger) {
|
|||
process.on('uncaughtException', cleanUp.bind({ handleError: true }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Instance of `nconf.Provider`
|
||||
* @type {Provider}
|
||||
*/
|
||||
let nconf = new Provider();
|
||||
|
||||
let argv_config =
|
||||
yargs
|
||||
.version(package_json.version)
|
||||
|
@ -190,8 +228,6 @@ let argv_config =
|
|||
}
|
||||
});
|
||||
|
||||
Promise.promisifyAll(nconf);
|
||||
|
||||
require(`${__dirname}/../src/nconf_load_env.js`)(nconf);
|
||||
nconf
|
||||
.argv(argv_config);
|
||||
|
@ -211,6 +247,10 @@ nconf.defaults(require(`${__dirname}/../src/default_config.js`));
|
|||
|
||||
let logLevel = nconf.get('logLevel');
|
||||
|
||||
/**
|
||||
* Instnace of `winston.Logger`
|
||||
* @type {Logger}
|
||||
*/
|
||||
let logger = winston.createLogger({
|
||||
level: logLevel,
|
||||
format: winston.format.simple(),
|
||||
|
@ -218,4 +258,8 @@ let logger = winston.createLogger({
|
|||
transports: [ new (winston.transports.Console)({ level: (logLevel !== 'null' ? logLevel : void(0)), silent: (logLevel === 'null') }) ]
|
||||
});
|
||||
|
||||
/**
|
||||
* Exports the main function for the application, a configured `nconf.Provider` instance and a winston logger
|
||||
* @module tor-router/launch
|
||||
*/
|
||||
module.exports = { main, nconf, logger };
|
|
@ -1,47 +1,63 @@
|
|||
const env_whitelist = [ "CONTROL_HOST",
|
||||
"TOR_PATH",
|
||||
"INSTANCES",
|
||||
"SOCKS_HOST",
|
||||
"DNS_HOST",
|
||||
"HTTP_HOST",
|
||||
"LOG_LEVEL",
|
||||
'PARENT_DATA_DIRECTORIES',
|
||||
'LOAD_BALANCE_METHOD',
|
||||
"WEBSOCKET_CONTROL_PORT",
|
||||
"PROXY_BY_NAME",
|
||||
"DENY_UNIDENTIFIED_USERS",
|
||||
"controlHost",
|
||||
"torPath",
|
||||
"instances",
|
||||
"socksHost",
|
||||
"dnsHost",
|
||||
"httpHost",
|
||||
"logLevel",
|
||||
'parentDataDirectories',
|
||||
'loadBalanceMethod',
|
||||
"websocketControlPort",
|
||||
"proxyByName",
|
||||
"denyUnidentifedUsers"
|
||||
];
|
||||
/**
|
||||
* An array of all valid environment variables
|
||||
* @constant
|
||||
* @type {string[]}
|
||||
*/
|
||||
const env_whitelist = [
|
||||
"CONTROL_HOST",
|
||||
"TOR_PATH",
|
||||
"INSTANCES",
|
||||
"SOCKS_HOST",
|
||||
"DNS_HOST",
|
||||
"HTTP_HOST",
|
||||
"LOG_LEVEL",
|
||||
'PARENT_DATA_DIRECTORIES',
|
||||
'LOAD_BALANCE_METHOD',
|
||||
"WEBSOCKET_CONTROL_PORT",
|
||||
"PROXY_BY_NAME",
|
||||
"DENY_UNIDENTIFIED_USERS"
|
||||
];
|
||||
|
||||
module.exports = (nconf) => {
|
||||
/**
|
||||
* Converts a configuration property's name from env variable format to application config format
|
||||
* `"CONTROL_HOST"` -> `"controlHost"`
|
||||
* @param {string} env - Environment variable
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
function env_to_config(env) {
|
||||
let a = env.toLowerCase().split('_');
|
||||
i = 1;
|
||||
while (i < a.length) {
|
||||
a[i] = a[i][0].toUpperCase() + a[i].substr(1);
|
||||
i++;
|
||||
}
|
||||
return a.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up nconf with the `env` store.
|
||||
* @param {Provider} nconf - Instance of `nconf.Provider`.
|
||||
* @returns {Provider} - Same instance of `nconf.Provider`.
|
||||
*/
|
||||
function setup_nconf_env(nconf) {
|
||||
return nconf
|
||||
.env({
|
||||
whitelist: env_whitelist,
|
||||
whitelist: env_whitelist.concat(env_whitelist.map(env_to_config)),
|
||||
parseValues: true,
|
||||
transform: (obj) => {
|
||||
if (env_whitelist.includes(obj.key)) {
|
||||
if (obj.key.indexOf('_') !== -1) {
|
||||
let a = obj.key.toLowerCase().split('_');
|
||||
i = 1;
|
||||
while (i < a.length) {
|
||||
a[i] = a[i][0].toUpperCase() + a[i].substr(1);
|
||||
i++;
|
||||
}
|
||||
obj.key = a.join('');
|
||||
obj.key = env_to_config(obj.key);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* This module returns a function
|
||||
* @module tor-router/nconf_load_env
|
||||
*/
|
||||
module.exports = setup_nconf_env;
|
|
@ -1,5 +1,9 @@
|
|||
const winston = require('winston');
|
||||
|
||||
/**
|
||||
* Contains a winston `Logger` that is silent (doesn't log anything).
|
||||
* @module tor-router/winston_silent_logger
|
||||
*/
|
||||
module.exports = winston.createLogger({
|
||||
level: 'info',
|
||||
format: winston.format.simple(),
|
|
@ -1,7 +1,8 @@
|
|||
|
||||
const _ = require('lodash');
|
||||
const { assert } = require('chai');
|
||||
const nconf = require('nconf');
|
||||
const { Provider } = require('nconf');
|
||||
const nconf = new Provider();
|
||||
const getPort = require('get-port');
|
||||
|
||||
nconf.use('memory');
|
||||
|
@ -9,7 +10,7 @@ require(`${__dirname}/../src/nconf_load_env.js`)(nconf);
|
|||
nconf.defaults(require(`${__dirname}/../src/default_config.js`));
|
||||
const { ControlServer, TorPool, HTTPServer, SOCKSServer, DNSServer } = require('../');
|
||||
|
||||
let controlServer = new ControlServer(null, nconf);
|
||||
let controlServer = new ControlServer(nconf);
|
||||
let controlPort;
|
||||
|
||||
describe('ControlServer', function () {
|
||||
|
@ -23,7 +24,7 @@ describe('ControlServer', function () {
|
|||
it('should create a TorPool with a given configuration', function () {
|
||||
let torPool = controlServer.createTorPool({ ProtocolWarnings: 1 });
|
||||
|
||||
assert.instanceOf(controlServer.torPool, TorPool);
|
||||
assert.instanceOf(controlServer.tor_pool, TorPool);
|
||||
assert.equal(1, torPool.default_tor_config.ProtocolWarnings);
|
||||
});
|
||||
});
|
||||
|
@ -56,6 +57,6 @@ describe('ControlServer', function () {
|
|||
});
|
||||
|
||||
after('shutdown tor pool', async function () {
|
||||
await controlServer.torPool.exit();
|
||||
await controlServer.tor_pool.exit();
|
||||
});
|
||||
});
|
|
@ -1,9 +1,11 @@
|
|||
|
||||
const nconf = require('nconf');
|
||||
const { Provider } = require('nconf');
|
||||
const nconf = new Provider();
|
||||
const getPort = require('get-port');
|
||||
const dns = require('native-dns');
|
||||
const { assert } = require('chai');
|
||||
|
||||
const { TorPool, DNSServer } = require('../');
|
||||
const { TorPool, DNSServer, TorProcess } = require('../');
|
||||
const { WAIT_FOR_CREATE } = require('./constants');
|
||||
|
||||
nconf.use('memory');
|
||||
|
@ -14,7 +16,7 @@ let dnsServerTorPool;
|
|||
let dnsServer;
|
||||
describe('DNSServer', function () {
|
||||
dnsServerTorPool = new TorPool(nconf.get('torPath'), {}, nconf.get('parentDataDirectory'), 'round_robin', null);
|
||||
dnsServer = new DNSServer(dnsServerTorPool, {}, 10000);
|
||||
dnsServer = new DNSServer(dnsServerTorPool, {}, nconf.get('dns:timeout'));
|
||||
let dnsPort;
|
||||
before('start up server', async function (){
|
||||
this.timeout(WAIT_FOR_CREATE);
|
||||
|
@ -48,6 +50,31 @@ describe('DNSServer', function () {
|
|||
|
||||
req.send();
|
||||
});
|
||||
|
||||
it('should emit the "instance_connection" event', function (done) {
|
||||
this.timeout(10000);
|
||||
|
||||
dnsServer.on('instance_connection', (instance, source) => {
|
||||
assert.instanceOf(instance, TorProcess);
|
||||
assert.isObject(source);
|
||||
done();
|
||||
});
|
||||
|
||||
let req = dns.Request({
|
||||
question: dns.Question({
|
||||
name: 'example.com',
|
||||
type: 'A'
|
||||
}),
|
||||
server: { address: '127.0.0.1', port: dnsPort, type: 'udp' },
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
req.on('timeout', function () {
|
||||
done(new Error('Connection timed out'));
|
||||
});
|
||||
|
||||
req.send();
|
||||
});
|
||||
});
|
||||
|
||||
after('shutdown server', function () {
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
const nconf = require('nconf');
|
||||
const request = require('request-promise');
|
||||
const { Provider } = require('nconf');
|
||||
const nconf = new Provider();
|
||||
const getPort = require('get-port');
|
||||
const { assert } = require('chai');
|
||||
const _ = require('lodash');
|
||||
const Promise = require('bluebird');
|
||||
|
||||
const { TorPool, HTTPServer } = require('../');
|
||||
const { WAIT_FOR_CREATE, PAGE_LOAD_TIME } = require('./constants');
|
||||
const { TorPool, HTTPServer, TorProcess } = require('../');
|
||||
const { WAIT_FOR_CREATE, PAGE_LOAD_TIME, RETRY_DELAY, RETRY_STRATEGY, MAX_ATTEMPTS } = require('./constants');
|
||||
|
||||
const request = require('requestretry').defaults({
|
||||
promiseFactory: ((resolver) => new Promise(resolver)),
|
||||
maxAttempts: MAX_ATTEMPTS,
|
||||
retryStrategy: RETRY_STRATEGY,
|
||||
retryDelay: RETRY_DELAY
|
||||
});
|
||||
|
||||
nconf.use('memory');
|
||||
require(`${__dirname}/../src/nconf_load_env.js`)(nconf);
|
||||
|
@ -39,6 +47,29 @@ describe('HTTPServer', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('should emit the "instance_connection" event', function (done) {
|
||||
this.timeout(PAGE_LOAD_TIME);
|
||||
|
||||
let connectionHandler = (instance, source) => {
|
||||
assert.instanceOf(instance, TorProcess);
|
||||
assert.isObject(source);
|
||||
|
||||
httpServer.removeAllListeners('instance_connection');;
|
||||
done();
|
||||
};
|
||||
|
||||
httpServer.on('instance_connection', connectionHandler);
|
||||
|
||||
request({
|
||||
url: 'http://example.com',
|
||||
proxy: `http://127.0.0.1:${httpPort}`
|
||||
})
|
||||
.catch((err) => {
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
after('shutdown server', function () {
|
||||
httpServer.close();
|
||||
});
|
||||
|
@ -48,7 +79,7 @@ describe('HTTPServer', function () {
|
|||
});
|
||||
|
||||
});
|
||||
|
||||
return
|
||||
describe('#handle_connect_connections(req, inbound_socket, head)', function () {
|
||||
let httpServerTorPool;
|
||||
let httpServer;
|
||||
|
@ -70,11 +101,33 @@ describe('HTTPServer', function () {
|
|||
this.timeout(PAGE_LOAD_TIME);
|
||||
|
||||
await request({
|
||||
url: 'http://example.com',
|
||||
url: 'https://example.com',
|
||||
proxy: `http://127.0.0.1:${httpPort}`
|
||||
});
|
||||
});
|
||||
|
||||
it('should emit the "instance_connection" event', function (done) {
|
||||
this.timeout(PAGE_LOAD_TIME);
|
||||
|
||||
let connectionHandler = (instance, source) => {
|
||||
assert.instanceOf(instance, TorProcess);
|
||||
assert.isObject(source);
|
||||
|
||||
httpServer.removeAllListeners('instance_connection');;
|
||||
done();
|
||||
};
|
||||
|
||||
httpServer.on('instance_connection', connectionHandler);
|
||||
|
||||
request({
|
||||
url: 'https://example.com',
|
||||
proxy: `http://127.0.0.1:${httpPort}`
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
after('shutdown server', function () {
|
||||
httpServer.close();
|
||||
});
|
||||
|
@ -117,11 +170,11 @@ describe('HTTPServer', function () {
|
|||
assert.isTrue(source.by_name);
|
||||
|
||||
req.cancel();
|
||||
httpServer.removeAllListeners('instance-connection');;
|
||||
httpServer.removeAllListeners('instance_connection');;
|
||||
done();
|
||||
};
|
||||
|
||||
httpServer.on('instance-connection', connectionHandler);
|
||||
httpServer.on('instance_connection', connectionHandler);
|
||||
|
||||
req = request({
|
||||
url: 'http://example.com',
|
||||
|
@ -129,8 +182,7 @@ describe('HTTPServer', function () {
|
|||
})
|
||||
.catch(done)
|
||||
});
|
||||
|
||||
|
||||
|
||||
it(`four requests made to example.com through the group named "foo" should come from the instances in "foo"`, function (done) {
|
||||
(async () => {
|
||||
this.timeout(PAGE_LOAD_TIME + (WAIT_FOR_CREATE));
|
||||
|
@ -145,8 +197,6 @@ describe('HTTPServer', function () {
|
|||
.then(async () => {
|
||||
httpServer.proxy_by_name.mode = "group";
|
||||
|
||||
let request = require('request-promise').defaults({ proxy: `http://foo:@127.0.0.1:${httpPort}` });
|
||||
|
||||
let names_requested = [];
|
||||
|
||||
let connectionHandler = (instance, source) => {
|
||||
|
@ -158,17 +208,18 @@ describe('HTTPServer', function () {
|
|||
let names_in_group = httpServerTorPool.instances_by_group('foo').map((i) => i.instance_name).sort()
|
||||
|
||||
assert.deepEqual(names_requested, names_in_group);
|
||||
httpServer.removeAllListeners('instance-connection');
|
||||
httpServer.removeAllListeners('instance_connection');
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
httpServer.on('instance-connection', connectionHandler);
|
||||
httpServer.on('instance_connection', connectionHandler);
|
||||
|
||||
let i = 0;
|
||||
while (i < httpServerTorPool.instances.length) {
|
||||
await request({
|
||||
url: 'http://example.com'
|
||||
url: 'http://example.com',
|
||||
proxy: `http://foo:@127.0.0.1:${httpPort}`
|
||||
});
|
||||
i++;
|
||||
}
|
||||
|
@ -183,11 +234,13 @@ describe('HTTPServer', function () {
|
|||
it(`shouldn't be able to send a request without a username`, async function() {
|
||||
let f = () => {};
|
||||
try {
|
||||
await request({
|
||||
let res = await request({
|
||||
url: 'http://example.com',
|
||||
proxy: `https://127.0.0.1:${httpPort}`,
|
||||
proxy: `http://127.0.0.1:${httpPort}`,
|
||||
timeout: PAGE_LOAD_TIME
|
||||
});
|
||||
if (res.statusCode === 407)
|
||||
throw new Error('407');
|
||||
} catch (error) {
|
||||
f = () => { throw error };
|
||||
} finally {
|
||||
|
@ -198,11 +251,14 @@ describe('HTTPServer', function () {
|
|||
it(`shouldn't be able to send a request with an incorrect username`, async function() {
|
||||
let f = () => {};
|
||||
try {
|
||||
await request({
|
||||
let res = await request({
|
||||
url: 'http://example.com',
|
||||
proxy: `http://blah-blah-blah:@127.0.0.1:${httpPort}`,
|
||||
timeout: PAGE_LOAD_TIME
|
||||
timeout: PAGE_LOAD_TIME,
|
||||
fullResponse: true
|
||||
});
|
||||
if (res.statusCode === 407)
|
||||
throw new Error('407');
|
||||
} catch (error) {
|
||||
f = () => { throw error };
|
||||
} finally {
|
||||
|
@ -218,7 +274,7 @@ describe('HTTPServer', function () {
|
|||
await httpServerTorPool.exit();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#authenticate_user_connect(req, socket)', function () {
|
||||
let httpServerTorPool;
|
||||
let httpServer;
|
||||
|
@ -250,11 +306,11 @@ describe('HTTPServer', function () {
|
|||
assert.equal(instance.instance_name, instance_def.Name);
|
||||
assert.isTrue(source.by_name);
|
||||
req.cancel();
|
||||
httpServer.removeAllListeners('instance-connection');
|
||||
httpServer.removeAllListeners('instance_connection');
|
||||
done();
|
||||
};
|
||||
|
||||
httpServer.on('instance-connection', connectionHandler);
|
||||
httpServer.on('instance_connection', connectionHandler);
|
||||
|
||||
req = request({
|
||||
url: 'https://example.com',
|
||||
|
@ -290,12 +346,12 @@ describe('HTTPServer', function () {
|
|||
let names_in_group = httpServerTorPool.instances_by_group('foo').map((i) => i.instance_name).sort()
|
||||
|
||||
assert.deepEqual(names_requested, names_in_group);
|
||||
httpServer.removeAllListeners('instance-connection');
|
||||
httpServer.removeAllListeners('instance_connection');
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
httpServer.on('instance-connection', connectionHandler);
|
||||
httpServer.on('instance_connection', connectionHandler);
|
||||
|
||||
let i = 0;
|
||||
while (i < httpServerTorPool.instances.length) {
|
||||
|
@ -323,7 +379,7 @@ describe('HTTPServer', function () {
|
|||
} catch (error) {
|
||||
f = () => { throw error };
|
||||
} finally {
|
||||
assert.throws(f, "socket hang up", "Did not hang up");
|
||||
assert.throws(f, "statusCode=407", "Did not return 407 status code");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -338,7 +394,7 @@ describe('HTTPServer', function () {
|
|||
} catch (error) {
|
||||
f = () => { throw error };
|
||||
} finally {
|
||||
assert.throws(f, "socket hang up", "Did not hang up");
|
||||
assert.throws(f, "statusCode=407", "Did not hang up");
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
const _ = require('lodash');
|
||||
const assert = require('chai').assert;
|
||||
const Promise = require('bluebird');
|
||||
const nconf = require('nconf');
|
||||
const { Provider } = require('nconf');
|
||||
const nconf = new Provider();
|
||||
const rpc = require('jrpc2');
|
||||
const getPort = require('get-port');
|
||||
const temp = require('temp');
|
||||
|
@ -16,7 +17,7 @@ const { WAIT_FOR_CREATE } = require('./constants');
|
|||
Promise.promisifyAll(fs);
|
||||
Promise.promisifyAll(temp);
|
||||
|
||||
let rpcControlServer = new ControlServer(null, nconf);
|
||||
let rpcControlServer = new ControlServer(nconf);
|
||||
let rpcControlPort;
|
||||
let rpcClient;
|
||||
|
||||
|
@ -91,7 +92,7 @@ describe('ControlServer - RPC Interface', function () {
|
|||
});
|
||||
|
||||
it("tor pool should now contain and instance that has the same name as the name specified in the defintion", function () {
|
||||
assert.ok(rpcControlServer.torPool.instance_by_name('instance-2'));
|
||||
assert.ok(rpcControlServer.tor_pool.instance_by_name('instance-2'));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -123,11 +124,11 @@ describe('ControlServer - RPC Interface', function () {
|
|||
});
|
||||
|
||||
it('"instance-1" should be added to "baz"', function () {
|
||||
assert.include(rpcControlServer.torPool.instances_by_group('baz').map((i) => i.instance_name), "instance-1");
|
||||
assert.include(rpcControlServer.tor_pool.instances_by_group('baz').map((i) => i.instance_name), "instance-1");
|
||||
});
|
||||
|
||||
after('remove from group', function () {
|
||||
rpcControlServer.torPool.groups['baz'].remove_by_name('instance-1');
|
||||
rpcControlServer.tor_pool.groups['baz'].remove_by_name('instance-1');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -137,17 +138,17 @@ describe('ControlServer - RPC Interface', function () {
|
|||
});
|
||||
|
||||
it('"instance-1" should be added to "baz"', function () {
|
||||
assert.include(rpcControlServer.torPool.instances_by_group('baz').map((i) => i.instance_name), "instance-1");
|
||||
assert.include(rpcControlServer.tor_pool.instances_by_group('baz').map((i) => i.instance_name), "instance-1");
|
||||
});
|
||||
|
||||
after('remove from group', function () {
|
||||
rpcControlServer.torPool.groups['baz'].remove_by_name('instance-1');
|
||||
rpcControlServer.tor_pool.groups['baz'].remove_by_name('instance-1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#removeInstanceFromGroupByName()', function () {
|
||||
before('add to group', function () {
|
||||
rpcControlServer.torPool.groups['baz'].add_by_name('instance-1');
|
||||
rpcControlServer.tor_pool.groups['baz'].add_by_name('instance-1');
|
||||
});
|
||||
|
||||
it(`should remove "instance-1" from baz`, async function () {
|
||||
|
@ -155,13 +156,13 @@ describe('ControlServer - RPC Interface', function () {
|
|||
});
|
||||
|
||||
it('"instance-1" should be remove from to "baz"', function () {
|
||||
assert.notInclude(rpcControlServer.torPool.instances_by_group('baz').map((i) => i.instance_name), "instance-1");
|
||||
assert.notInclude(rpcControlServer.tor_pool.group_names, "baz");
|
||||
});
|
||||
});
|
||||
|
||||
describe('#removeInstanceFromGroupAt()', function () {
|
||||
before('add to group', function () {
|
||||
rpcControlServer.torPool.groups['baz'].add_by_name('instance-1');
|
||||
rpcControlServer.tor_pool.groups['baz'].add_by_name('instance-1');
|
||||
});
|
||||
|
||||
it(`should remove "instance-1" from baz`, async function () {
|
||||
|
@ -169,10 +170,9 @@ describe('ControlServer - RPC Interface', function () {
|
|||
});
|
||||
|
||||
it('"instance-1" should be remove from to "baz"', function () {
|
||||
assert.notInclude(rpcControlServer.torPool.instances_by_group('baz').map((i) => i.instance_name), "instance-1");
|
||||
assert.notInclude(rpcControlServer.tor_pool.group_names, "baz");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#newIdentites()', function () {
|
||||
this.timeout(3000);
|
||||
|
@ -209,7 +209,7 @@ describe('ControlServer - RPC Interface', function () {
|
|||
});
|
||||
|
||||
it('all instances should have the modified variables', async function() {
|
||||
await Promise.all(rpcControlServer.torPool.instances.map(async (instance) => {
|
||||
await Promise.all(rpcControlServer.tor_pool.instances.map(async (instance) => {
|
||||
let var1 = await instance.get_config('TestSocks');
|
||||
let var2 = await instance.get_config('ProtocolWarnings');
|
||||
|
||||
|
@ -219,8 +219,8 @@ describe('ControlServer - RPC Interface', function () {
|
|||
});
|
||||
|
||||
after('unset config variables', async function () {
|
||||
await rpcControlServer.torPool.set_config_all('TestSocks', 0);
|
||||
await rpcControlServer.torPool.set_config_all('ProtocolWarnings', 0);
|
||||
await rpcControlServer.tor_pool.set_config_all('TestSocks', 0);
|
||||
await rpcControlServer.tor_pool.set_config_all('ProtocolWarnings', 0);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -245,13 +245,13 @@ describe('ControlServer - RPC Interface', function () {
|
|||
});
|
||||
|
||||
it('all instances should have the config value set', async function () {
|
||||
let values = _.flatten(await Promise.all(rpcControlServer.torPool.instances_by_group('foo').map((i) => i.get_config('ProtocolWarnings'))));
|
||||
let values = _.flatten(await Promise.all(rpcControlServer.tor_pool.instances_by_group('foo').map((i) => i.get_config('ProtocolWarnings'))));
|
||||
|
||||
assert.isTrue(values.every((v) => v === "1"));
|
||||
});
|
||||
|
||||
after('unset config values', async function () {
|
||||
rpcControlServer.torPool.set_config_by_group('foo', 'ProtocolWarnings', 0);
|
||||
rpcControlServer.tor_pool.set_config_by_group('foo', 'ProtocolWarnings', 0);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -337,7 +337,7 @@ describe('ControlServer - RPC Interface', function () {
|
|||
describe('#getLoadBalanceMethod()', function () {
|
||||
this.timeout(3000);
|
||||
before(function () {
|
||||
rpcControlServer.torPool.load_balance_method = 'round_robin';
|
||||
rpcControlServer.tor_pool.load_balance_method = 'round_robin';
|
||||
});
|
||||
|
||||
it('should return the current load balance method', async function () {
|
||||
|
@ -355,11 +355,11 @@ describe('ControlServer - RPC Interface', function () {
|
|||
});
|
||||
|
||||
it('the load balance method should be changed', function () {
|
||||
assert.equal(rpcControlServer.torPool.load_balance_method, 'weighted');
|
||||
assert.equal(rpcControlServer.tor_pool.load_balance_method, 'weighted');
|
||||
});
|
||||
|
||||
after(function () {
|
||||
rpcControlServer.torPool.load_balance_method = 'round_robin';
|
||||
rpcControlServer.tor_pool.load_balance_method = 'round_robin';
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -367,7 +367,7 @@ describe('ControlServer - RPC Interface', function () {
|
|||
this.timeout(3000);
|
||||
|
||||
before('set config property', async function () {
|
||||
await rpcControlServer.torPool.instance_by_name('instance-1').set_config('TestSocks', 1);
|
||||
await rpcControlServer.tor_pool.instance_by_name('instance-1').set_config('TestSocks', 1);
|
||||
});
|
||||
|
||||
it('should retrieve the property from the tor instance', async function () {
|
||||
|
@ -379,7 +379,7 @@ describe('ControlServer - RPC Interface', function () {
|
|||
});
|
||||
|
||||
after('unset config property', async function () {
|
||||
await rpcControlServer.torPool.instance_by_name('instance-1').set_config('TestSocks', 0);
|
||||
await rpcControlServer.tor_pool.instance_by_name('instance-1').set_config('TestSocks', 0);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -387,7 +387,7 @@ describe('ControlServer - RPC Interface', function () {
|
|||
this.timeout(3000);
|
||||
|
||||
before('set config property', async function () {
|
||||
await rpcControlServer.torPool.instance_at(0).set_config('TestSocks', 1);
|
||||
await rpcControlServer.tor_pool.instance_at(0).set_config('TestSocks', 1);
|
||||
});
|
||||
|
||||
it('should retrieve the property from the tor instance', async function () {
|
||||
|
@ -399,7 +399,7 @@ describe('ControlServer - RPC Interface', function () {
|
|||
});
|
||||
|
||||
after('unset config property', async function () {
|
||||
await rpcControlServer.torPool.instance_at(0).set_config('TestSocks', 0);
|
||||
await rpcControlServer.tor_pool.instance_at(0).set_config('TestSocks', 0);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -407,7 +407,7 @@ describe('ControlServer - RPC Interface', function () {
|
|||
this.timeout(3000);
|
||||
|
||||
before('set config property', async function () {
|
||||
await rpcControlServer.torPool.instance_by_name('instance-1').set_config('TestSocks', 0);
|
||||
await rpcControlServer.tor_pool.instance_by_name('instance-1').set_config('TestSocks', 0);
|
||||
});
|
||||
|
||||
it('should set the property for the tor instance', async function () {
|
||||
|
@ -415,12 +415,12 @@ describe('ControlServer - RPC Interface', function () {
|
|||
});
|
||||
|
||||
it('tor instance should have the modified property', async function () {
|
||||
let value = await rpcControlServer.torPool.instance_by_name('instance-1').get_config('TestSocks');
|
||||
let value = await rpcControlServer.tor_pool.instance_by_name('instance-1').get_config('TestSocks');
|
||||
assert.equal(value, 1);
|
||||
});
|
||||
|
||||
after('unset config property', async function () {
|
||||
await rpcControlServer.torPool.instance_by_name('instance-1').set_config('TestSocks', 0);
|
||||
await rpcControlServer.tor_pool.instance_by_name('instance-1').set_config('TestSocks', 0);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -428,7 +428,7 @@ describe('ControlServer - RPC Interface', function () {
|
|||
this.timeout(3000);
|
||||
|
||||
before('set config property', async function () {
|
||||
await rpcControlServer.torPool.instance_at(0).set_config('TestSocks', 0);
|
||||
await rpcControlServer.tor_pool.instance_at(0).set_config('TestSocks', 0);
|
||||
});
|
||||
|
||||
it('should set the property for the tor instance', async function () {
|
||||
|
@ -436,12 +436,12 @@ describe('ControlServer - RPC Interface', function () {
|
|||
});
|
||||
|
||||
it('tor instance should have the modified property', async function () {
|
||||
let value = await rpcControlServer.torPool.instance_at(0).get_config('TestSocks');
|
||||
let value = await rpcControlServer.tor_pool.instance_at(0).get_config('TestSocks');
|
||||
assert.equal(value, 1);
|
||||
});
|
||||
|
||||
after('unset config property', async function () {
|
||||
await rpcControlServer.torPool.instance_at(0).set_config('TestSocks', 0);
|
||||
await rpcControlServer.tor_pool.instance_at(0).set_config('TestSocks', 0);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -476,31 +476,31 @@ describe('ControlServer - RPC Interface', function () {
|
|||
this.timeout(3000);
|
||||
let instance_name;
|
||||
it('should rotate the 0th item in the instances array', async function () {
|
||||
instance_name = rpcControlServer.torPool.instances[0].instance_name;
|
||||
instance_name = rpcControlServer.tor_pool.instances[0].instance_name;
|
||||
await rpcClient.invokeAsync('nextInstance', []);
|
||||
});
|
||||
|
||||
it('0th item in the instances array should be different after nextInstance is called', function () {
|
||||
assert.notEqual(rpcControlServer.torPool.instances[0].instance_name, instance_name);
|
||||
assert.notEqual(rpcControlServer.tor_pool.instances[0].instance_name, instance_name);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#nextInstanceByGroup(group)', function () {
|
||||
before('add "instance-1" to "foo"', function () {
|
||||
rpcControlServer.torPool.add_instance_to_group_by_name('foo', 'instance-2');
|
||||
rpcControlServer.tor_pool.add_instance_to_group_by_name('foo', 'instance-2');
|
||||
});
|
||||
|
||||
it('should rotate the instances in group "foo"', async function () {
|
||||
this.timeout(5000);
|
||||
let first_instance_name_before = rpcControlServer.torPool.groups['foo'][0].instance_name;
|
||||
let first_instance_name_before = rpcControlServer.tor_pool.groups['foo'][0].instance_name;
|
||||
await rpcClient.invokeAsync('nextInstanceByGroup', [ 'foo' ]);
|
||||
let first_instance_name_after = rpcControlServer.torPool.groups['foo'][0].instance_name;
|
||||
let first_instance_name_after = rpcControlServer.tor_pool.groups['foo'][0].instance_name;
|
||||
|
||||
assert.notEqual(first_instance_name_after, first_instance_name_before);
|
||||
});
|
||||
|
||||
after('remove "instance-1" from "foo"', function () {
|
||||
rpcControlServer.torPool.remove_instance_from_group_by_name('foo', 'instance-2');
|
||||
rpcControlServer.tor_pool.remove_instance_from_group_by_name('foo', 'instance-2');
|
||||
})
|
||||
});
|
||||
|
||||
|
@ -508,41 +508,41 @@ describe('ControlServer - RPC Interface', function () {
|
|||
describe('#removeInstanceAt(index)', function () {
|
||||
this.timeout(10000);
|
||||
it("should remove an instance at the position specified", async function () {
|
||||
instance_num1 = rpcControlServer.torPool.instances.length;
|
||||
instance_num1 = rpcControlServer.tor_pool.instances.length;
|
||||
await rpcClient.invokeAsync('removeInstanceAt', [0]);
|
||||
});
|
||||
|
||||
it('the tor pool should contain one instance fewer', function () {
|
||||
assert.equal(rpcControlServer.torPool.instances.length, (instance_num1 - 1));
|
||||
assert.equal(rpcControlServer.tor_pool.instances.length, (instance_num1 - 1));
|
||||
});
|
||||
});
|
||||
|
||||
describe('#removeInstanceByName(instance_name)', function () {
|
||||
this.timeout(10000);
|
||||
it("should remove an instance at the position specified", async function () {
|
||||
instance_num2 = rpcControlServer.torPool.instances.length;
|
||||
instance_num2 = rpcControlServer.tor_pool.instances.length;
|
||||
await rpcClient.invokeAsync('removeInstanceByName', [ "instance-1" ]);
|
||||
});
|
||||
|
||||
it('the tor pool should contain one instance fewer', function () {
|
||||
assert.equal(rpcControlServer.torPool.instances.length, (instance_num2 - 1));
|
||||
assert.equal(rpcControlServer.tor_pool.instances.length, (instance_num2 - 1));
|
||||
});
|
||||
});
|
||||
|
||||
describe('#closeInstances()', function () {
|
||||
this.timeout(10000);
|
||||
it('should shutdown all instances', async function () {
|
||||
instance_num = rpcControlServer.torPool.instances.length;
|
||||
instance_num = rpcControlServer.tor_pool.instances.length;
|
||||
await rpcClient.invokeAsync('closeInstances', [ ]);
|
||||
});
|
||||
|
||||
it('no instances should be present in the pool', function () {
|
||||
assert.equal(rpcControlServer.torPool.instances.length, 0);
|
||||
assert.equal(rpcControlServer.tor_pool.instances.length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
after('shutdown tor pool', async function () {
|
||||
this.timeout(10000);
|
||||
await rpcControlServer.torPool.exit();
|
||||
await rpcControlServer.tor_pool.exit();
|
||||
});
|
||||
});
|
|
@ -1,12 +1,20 @@
|
|||
const nconf = require('nconf');
|
||||
const request = require('request-promise');
|
||||
const { Provider } = require('nconf');
|
||||
const nconf = new Provider();
|
||||
const getPort = require('get-port');
|
||||
const { HttpAgent, auth } = require('socksv5');
|
||||
const { assert } = require('chai');
|
||||
const SocksProxyAgent = require('socks-proxy-agent');
|
||||
const _ = require('lodash');
|
||||
|
||||
const { TorPool, SOCKSServer } = require('../');
|
||||
const { WAIT_FOR_CREATE, PAGE_LOAD_TIME } = require('./constants');
|
||||
const { TorPool, SOCKSServer, TorProcess } = require('../');
|
||||
const { WAIT_FOR_CREATE, PAGE_LOAD_TIME, RETRY_DELAY, RETRY_STRATEGY, MAX_ATTEMPTS } = require('./constants');
|
||||
|
||||
const request = require('requestretry').defaults({
|
||||
promiseFactory: ((resolver) => new Promise(resolver)),
|
||||
maxAttempts: MAX_ATTEMPTS,
|
||||
retryStrategy: RETRY_STRATEGY,
|
||||
retryDelay: RETRY_DELAY
|
||||
});
|
||||
|
||||
nconf.use('memory');
|
||||
require(`${__dirname}/../src/nconf_load_env.js`)(nconf);
|
||||
|
@ -21,7 +29,7 @@ describe('SOCKSServer', function () {
|
|||
before('start up server', async function (){
|
||||
socksServerTorPool = new TorPool(nconf.get('torPath'), {}, nconf.get('parentDataDirectory'), 'round_robin', null);
|
||||
socksServer = new SOCKSServer(socksServerTorPool, null, false);
|
||||
|
||||
|
||||
this.timeout(WAIT_FOR_CREATE);
|
||||
|
||||
await socksServerTorPool.create(1);
|
||||
|
@ -29,25 +37,37 @@ describe('SOCKSServer', function () {
|
|||
|
||||
await socksServer.listen(socksPort);
|
||||
});
|
||||
|
||||
it('should service a request for example.com', async function () {
|
||||
this.timeout(PAGE_LOAD_TIME);
|
||||
|
||||
await request({
|
||||
url: 'http://example.com',
|
||||
agent: new HttpAgent({
|
||||
proxyHost: '127.0.0.1',
|
||||
proxyPort: socksPort,
|
||||
localDNS: false,
|
||||
auths: [ auth.None() ]
|
||||
})
|
||||
agent: new SocksProxyAgent(`socks5h://127.0.0.1:${socksPort}`)
|
||||
});
|
||||
});
|
||||
|
||||
after('shutdown server', function () {
|
||||
socksServer.close();
|
||||
it('should emit the "instance_connection" event', function (done) {
|
||||
this.timeout(PAGE_LOAD_TIME);
|
||||
|
||||
let connectionHandler = (instance, source) => {
|
||||
assert.instanceOf(instance, TorProcess);
|
||||
assert.isObject(source);
|
||||
|
||||
socksServer.removeAllListeners('instance_connection');;
|
||||
done();
|
||||
};
|
||||
|
||||
socksServer.on('instance_connection', connectionHandler);
|
||||
|
||||
request({
|
||||
url: 'http://example.com',
|
||||
agent: new SocksProxyAgent(`socks5h://127.0.0.1:${socksPort}`)
|
||||
})
|
||||
.catch(done)
|
||||
});
|
||||
|
||||
after('shutdown tor pool', async function () {
|
||||
after('shutdown server and shutdown tor pool', async function () {
|
||||
socksServer.close();
|
||||
await socksServerTorPool.exit();
|
||||
});
|
||||
|
@ -79,29 +99,23 @@ describe('SOCKSServer', function () {
|
|||
it(`should service a request for example.com through ${instance_def.Name}`, function (done) {
|
||||
this.timeout(PAGE_LOAD_TIME);
|
||||
|
||||
let req;
|
||||
let connectionHandler = (instance, source) => {
|
||||
assert.equal(instance.instance_name, instance_def.Name);
|
||||
assert.isTrue(source.by_name);
|
||||
req.cancel();
|
||||
socksServer.removeAllListeners('instance-connection');
|
||||
socksServer.removeAllListeners('instance_connection');
|
||||
done();
|
||||
};
|
||||
|
||||
socksServer.on('instance-connection', connectionHandler);
|
||||
|
||||
req = request({
|
||||
socksServer.on('instance_connection', connectionHandler);
|
||||
request({
|
||||
url: 'http://example.com',
|
||||
agent: new HttpAgent({
|
||||
proxyHost: '127.0.0.1',
|
||||
proxyPort: socksPort,
|
||||
localDNS: false,
|
||||
auths: [ auth.UserPassword(instance_def.Name, "doesn't mater") ]
|
||||
})
|
||||
agent: new SocksProxyAgent(`socks5h://${instance_def.Name}:doesnotmatter@127.0.0.1:${socksPort}`)
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
return
|
||||
|
||||
it(`four requests made to example.com through the group named "foo" should come from the instances in "foo"`, function (done) {
|
||||
(async () => {
|
||||
this.timeout(PAGE_LOAD_TIME + (WAIT_FOR_CREATE));
|
||||
|
@ -116,15 +130,6 @@ describe('SOCKSServer', function () {
|
|||
.then(async () => {
|
||||
socksServer.proxy_by_name.mode = "group";
|
||||
|
||||
let request = require('request-promise').defaults({
|
||||
agent: new HttpAgent({
|
||||
proxyHost: '127.0.0.1',
|
||||
proxyPort: socksPort,
|
||||
localDNS: false,
|
||||
auths: [ auth.UserPassword('foo', "doesn't mater") ]
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
let names_requested = [];
|
||||
|
||||
|
@ -137,17 +142,18 @@ describe('SOCKSServer', function () {
|
|||
let names_in_group = socksServerTorPool.instances_by_group('foo').map((i) => i.instance_name).sort()
|
||||
|
||||
assert.deepEqual(names_requested, names_in_group);
|
||||
socksServer.removeAllListeners('instance-connection');
|
||||
socksServer.removeAllListeners('instance_connection');
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
socksServer.on('instance-connection', connectionHandler);
|
||||
socksServer.on('instance_connection', connectionHandler);
|
||||
|
||||
let i = 0;
|
||||
while (i < socksServerTorPool.instances.length) {
|
||||
await request({
|
||||
url: 'http://example.com'
|
||||
url: 'http://example.com',
|
||||
agent: new SocksProxyAgent(`socks5h://foo:doesnotmatter@127.0.0.1:${socksPort}`)
|
||||
});
|
||||
i++;
|
||||
}
|
||||
|
@ -199,11 +205,8 @@ describe('SOCKSServer', function () {
|
|||
// }
|
||||
// });
|
||||
|
||||
after('shutdown server', function () {
|
||||
after('shutdown server and shutdown tor pool', async function () {
|
||||
socksServer.close();
|
||||
});
|
||||
|
||||
after('shutdown tor pool', async function () {
|
||||
await socksServerTorPool.exit();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const nconf = require('nconf');
|
||||
const { Provider } = require('nconf');
|
||||
const nconf = new Provider();
|
||||
const { assert } = require('chai');
|
||||
const Promise = require('bluebird');
|
||||
const _ = require('lodash');
|
||||
|
@ -605,10 +606,10 @@ describe('TorPool', function () {
|
|||
torPool = torPoolFactory();
|
||||
|
||||
this.timeout(WAIT_FOR_CREATE);
|
||||
await torPool.create(1);
|
||||
await torPool.create([ { "Name": "instance-1", "Group": ["foo"] }, { "Name": "instance-2", "Group": ["foo"] } ]);
|
||||
});
|
||||
|
||||
it('should send a signal to an instance identified by name', async function () {
|
||||
it('should send a signal to a group of instances', async function () {
|
||||
this.timeout(5000);
|
||||
await torPool.signal_by_group('foo', 'DEBUG');
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const nconf = require('nconf');
|
||||
const { Provider } = require('nconf');
|
||||
const nconf = new Provider();
|
||||
const { assert } = require('chai');
|
||||
const _ = require('lodash');
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
module.exports = {
|
||||
WAIT_FOR_CREATE: 240000,
|
||||
PAGE_LOAD_TIME: 60000
|
||||
WAIT_FOR_CREATE: 600000,
|
||||
PAGE_LOAD_TIME: 60000,
|
||||
RETRY_DELAY: 500,
|
||||
MAX_ATTEMPTS: 10,
|
||||
RETRY_STRATEGY: require('requestretry').RetryStrategies.HTTPOrNetworkError
|
||||
};
|
|
@ -1,9 +1,9 @@
|
|||
describe("TorRouter", function () {
|
||||
require('./TorProcess');
|
||||
require('./TorPool');
|
||||
// require('./TorProcess');
|
||||
// require('./TorPool');
|
||||
require('./SOCKSServer');
|
||||
require('./HTTPServer');
|
||||
require('./DNSServer');
|
||||
require('./ControlServer');
|
||||
require('./RPCInterface');
|
||||
// require('./DNSServer');
|
||||
// require('./ControlServer');
|
||||
// require('./RPCInterface');
|
||||
});
|
Loading…
Reference in New Issue