dns-over-tls
Version:
dns-over-tls for node
109 lines (108 loc) • 4.4 kB
JavaScript
;
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
// const tls = require('tls');
// const crypto = require('crypto');
// const dnsPacket = require('dns-packet');
const crypto_1 = require("crypto");
const dnsPacket = __importStar(require("dns-packet"));
const tls_1 = require("tls");
exports.TWO_BYTES = 2;
exports.DEFAULT_TYPE = 'A';
exports.DEFAULT_PORT = 853;
exports.DEFAULT_HOST = '1.1.1.1';
exports.DEFAULT_CLASS = 'IN';
exports.DEFAULT_SERVERNAME = 'cloudflare-dns.com';
exports.RECURSION_DESIRED = dnsPacket.RECURSION_DESIRED;
exports.randomId = () => crypto_1.randomBytes(exports.TWO_BYTES).readUInt16BE(0);
exports.checkDone = ({ response, packetLength, socket, resolve, }) => {
// Why + TWO_BYTES? See comment in query()
if (response.length === packetLength + exports.TWO_BYTES) {
socket.destroy();
resolve(dnsPacket.streamDecode(response));
}
};
exports.getDnsQuery = ({ type, name, klass, id }) => ({
flags: exports.RECURSION_DESIRED,
id,
questions: [
{
class: klass,
name,
type,
},
],
type: 'query',
});
function query(...args) {
return new Promise((resolve, reject) => {
const { host, servername, name, klass, type, port } = exports.argsOrder(args);
let response = new Buffer(0);
let packetLength = 0;
const id = exports.randomId();
const dnsQuery = exports.getDnsQuery({ type, name, klass, id });
const dnsQueryBuf = dnsPacket.streamEncode(dnsQuery);
const socket = tls_1.connect({ host, servername, port });
socket.on('error', reject);
socket.on('secureConnect', () => socket.write(dnsQueryBuf));
socket.on('data', (data) => {
if (response.length === 0) {
// https://tools.ietf.org/html/rfc7858#section-3.3
// https://tools.ietf.org/html/rfc1035#section-4.2.2
// The message is prefixed with a two byte length field which gives the
// message length, excluding the two byte length field.
packetLength = data.readUInt16BE(0);
if (packetLength < 12) {
reject('Below DNS minimum packet length (DNS Header is 12 bytes)');
}
response = Buffer.from(data);
exports.checkDone({ response, packetLength, socket, resolve });
}
else {
response = Buffer.concat([response, data]);
exports.checkDone({ response, packetLength, socket, resolve });
}
});
});
}
exports.query = query;
exports.isObject = (obj) => obj === Object(obj);
exports.isString = (obj) => Object.prototype.toString.call(obj) === '[object String]';
exports.argsOrder = (args) => {
if (exports.isObject(args[0])) {
const options = args[0];
const { host, servername, name, klass = exports.DEFAULT_CLASS, type = exports.DEFAULT_TYPE, port = exports.DEFAULT_PORT, } = options;
if (!options.host || !options.servername || !options.name) {
throw new Error('At least host, servername and name must be set.');
}
return { host, servername, name, klass, type, port };
}
else if (args.length === 3) {
const host = args[0];
const servername = args[1];
const name = args[2];
const klass = exports.DEFAULT_CLASS;
const type = exports.DEFAULT_TYPE;
const port = exports.DEFAULT_PORT;
return { host, servername, name, klass, type, port };
}
else if (args.length === 1) {
const name = args[0];
const host = exports.DEFAULT_HOST;
const servername = exports.DEFAULT_SERVERNAME;
const klass = exports.DEFAULT_CLASS;
const type = exports.DEFAULT_TYPE;
const port = exports.DEFAULT_PORT;
return { host, servername, name, klass, type, port };
}
else {
throw new Error('Either an options object, a tuple of host, servername, name or one domain name are valid inputs.');
}
};
exports.default = { query };