tor-router/src/DNSServer.js

136 lines
3.8 KiB
JavaScript

const dns = 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;
if (!args[1])
args[1] = null;
return await new Promise((resolve, reject) => {
args.push(() => {
let args = Array.from(arguments);
resolve.apply(args);
});
try {
inner_func.apply(this, args);
} catch (error) {
reject(error);
}
});
}
/**
* 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) {
/**
* 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) => {
for (let question of req.question) {
let dns_port = (tor_instance.dns_port);
let outbound_req = Request({
question,
server: { address: '127.0.0.1', port: dns_port, type: 'udp' },
timeout: this.dns_timeout
});
outbound_req.on('message', (err, answer) => {
if (!err && answer) {
for (let a of answer.answer){
res.answer.push(a);
}
}
});
outbound_req.on('error', (err) => {
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();
});
outbound_req.send();
};
};
if (this.tor_pool.instances.length) {
connect(this.tor_pool.next());
}
else {
this.logger.debug(`[dns]: 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(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;