dns2
Version:
A DNS Server and Client Implementation in Pure JavaScript with no dependencies.
119 lines (107 loc) • 3.1 kB
JavaScript
const EventEmitter = require('node:events');
const Packet = require('../packet');
const DOHServer = require('./doh');
const TCPServer = require('./tcp');
const UDPServer = require('./udp');
class DNSServer extends EventEmitter {
constructor(options = {}) {
super();
this.servers = {};
if (options.doh) {
this.servers.doh = new DOHServer(options.doh).on('error', error =>
this.emit('error', error, 'doh'),
);
}
if (options.tcp) {
this.servers.tcp = new TCPServer().on('error', error =>
this.emit('error', error, 'tcp'),
);
}
if (options.udp) {
this.servers.udp = new UDPServer(
typeof options.udp === 'object' ? options.udp : undefined,
).on('error', error => this.emit('error', error, 'udp'));
}
const servers = Object.values(this.servers);
this.closed = Promise.all(
servers.map(
server => new Promise(resolve => server.once('close', resolve)),
),
).then(() => {
this.emit('close');
});
this.listening = Promise.all(
servers.map(
server => new Promise(resolve => server.once('listening', resolve)),
),
).then(() => {
const addresses = this.addresses();
this.emit('listening', addresses);
return addresses;
});
const maxConcurrent = options.maxConcurrent > 0 ? options.maxConcurrent : 0;
let active = 0;
const emitRequest = (request, send, client) => {
if (maxConcurrent && active >= maxConcurrent) {
const response = Packet.createResponseFromRequest(request);
response.header.rcode = Packet.RCODE.SERVFAIL;
send(response);
return;
}
active++;
const wrappedSend = (...args) => {
active--;
return send(...args);
};
this.emit('request', request, wrappedSend, client);
};
const emitRequestError = error => this.emit('requestError', error);
for (const server of servers) {
server.on('request', emitRequest);
server.on('requestError', emitRequestError);
}
if (options.handle) {
this.on('request', options.handle.bind(options));
}
}
addresses() {
const addresses = {};
const { udp, tcp, doh } = this.servers;
if (udp) {
addresses.udp = udp.address();
}
if (tcp) {
addresses.tcp = tcp.address();
}
if (doh) {
addresses.doh = doh.address();
}
return addresses;
}
listen(options = {}) {
for (const serverType of Object.keys(this.servers)) {
const server = this.servers[serverType];
const serverOptions = options[serverType]; // Port or { port, address }
if (serverOptions && serverOptions.port) {
server.listen(serverOptions.port, serverOptions.address);
} else {
server.listen(serverOptions);
}
}
return this.listening;
}
close() {
const { doh, udp, tcp } = this.servers;
if (udp) {
udp.close();
}
if (tcp) {
tcp.close();
}
if (doh) {
doh.close();
}
return this.closed;
}
}
module.exports = DNSServer;