@esutils/dns-packet
Version:
A minimal dns-packet library that implemented in `typescript`
1,244 lines (1,186 loc) • 45.4 kB
JavaScript
/******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ "./examples/dns-util.ts":
/*!******************************!*\
!*** ./examples/dns-util.ts ***!
\******************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "queryDNS": () => (/* binding */ queryDNS),
/* harmony export */ "queryMultipleDNS": () => (/* binding */ queryMultipleDNS)
/* harmony export */ });
/* harmony import */ var dgram__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! dgram */ "dgram");
/* harmony import */ var dgram__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(dgram__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _esutils_dns_packet__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @esutils/dns-packet */ "./dist/mjs/index.js");
/* TODO: Add abort signal */
async function queryDNS(dnsServerIp, port, questions) {
const query = _esutils_dns_packet__WEBPACK_IMPORTED_MODULE_1__.Packet.create();
query.header.id = _esutils_dns_packet__WEBPACK_IMPORTED_MODULE_1__.Packet.randomHeaderId();
// https://github.com/song940/node-dns/issues/29
query.header.rd = 1;
query.questions = questions;
const client = dgram__WEBPACK_IMPORTED_MODULE_0__.createSocket('udp4');
let clientClosed = false;
let clientRejected = false;
let dnsResolved = false;
const { name } = questions[0];
return new Promise((resolve, reject) => {
function done(timer) {
clearTimeout(timer);
if (!clientClosed) {
clientClosed = true;
client.close();
}
}
function raiseError(err) {
if (!clientRejected) {
clientRejected = true;
reject(err);
}
}
const timer = setTimeout(() => {
done(timer);
raiseError(new Error(`request timedout for ${name}`));
}, 10000);
client.once('message', (message) => {
dnsResolved = true;
done(timer);
const response = _esutils_dns_packet__WEBPACK_IMPORTED_MODULE_1__.Packet.decode(message, _esutils_dns_packet__WEBPACK_IMPORTED_MODULE_1__.decodeResponseDefault);
if (response.answers.length === 0) {
raiseError(new Error(`no answer for ${name}`));
return;
}
resolve({
ip: dnsServerIp,
packet: response,
});
});
client.once('close', () => {
if (!dnsResolved) {
raiseError(new Error('dns not retrieved'));
}
});
client.once('error', raiseError);
// DNS request timeout to 10 seconds
const buf = _esutils_dns_packet__WEBPACK_IMPORTED_MODULE_1__.Packet.encode(query, _esutils_dns_packet__WEBPACK_IMPORTED_MODULE_1__.encodeResponseDefault);
client.send(buf, port, dnsServerIp, (err) => err && raiseError(err));
});
}
async function queryMultipleDNS(servers, questions) {
const promises = [];
for (let i = 0; i < servers.length; i += 1) {
promises.push(queryDNS(servers[i].ip, servers[i].port, questions));
}
return Promise.any(promises);
}
/***/ }),
/***/ "dgram":
/*!************************!*\
!*** external "dgram" ***!
\************************/
/***/ ((module) => {
module.exports = require("dgram");
/***/ }),
/***/ "fs":
/*!*********************!*\
!*** external "fs" ***!
\*********************/
/***/ ((module) => {
module.exports = require("fs");
/***/ }),
/***/ "./dist/mjs/dns-packet.js":
/*!********************************!*\
!*** ./dist/mjs/dns-packet.js ***!
\********************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "BufferReader": () => (/* reexport safe */ _reader__WEBPACK_IMPORTED_MODULE_1__.BufferReader),
/* harmony export */ "BufferWriter": () => (/* reexport safe */ _writer__WEBPACK_IMPORTED_MODULE_2__.BufferWriter),
/* harmony export */ "CLASS": () => (/* binding */ CLASS),
/* harmony export */ "EDNS_OPTION_CODE": () => (/* binding */ EDNS_OPTION_CODE),
/* harmony export */ "Header": () => (/* binding */ Header),
/* harmony export */ "Name": () => (/* binding */ Name),
/* harmony export */ "Packet": () => (/* binding */ Packet),
/* harmony export */ "Question": () => (/* binding */ Question),
/* harmony export */ "Resource": () => (/* binding */ Resource),
/* harmony export */ "ResourceA": () => (/* binding */ ResourceA),
/* harmony export */ "ResourceAAAA": () => (/* binding */ ResourceAAAA),
/* harmony export */ "ResourceCNAME": () => (/* binding */ ResourceCNAME),
/* harmony export */ "ResourceEDNS": () => (/* binding */ ResourceEDNS),
/* harmony export */ "ResourceMX": () => (/* binding */ ResourceMX),
/* harmony export */ "ResourceNS": () => (/* binding */ ResourceNS),
/* harmony export */ "ResourceSOA": () => (/* binding */ ResourceSOA),
/* harmony export */ "ResourceSPF": () => (/* binding */ ResourceSPF),
/* harmony export */ "ResourceSRV": () => (/* binding */ ResourceSRV),
/* harmony export */ "TYPE": () => (/* binding */ TYPE),
/* harmony export */ "TYPE_INVERTED": () => (/* binding */ TYPE_INVERTED),
/* harmony export */ "createDnsBasic": () => (/* binding */ createDnsBasic),
/* harmony export */ "decodeResponseDefault": () => (/* binding */ decodeResponseDefault),
/* harmony export */ "encodeResponseDefault": () => (/* binding */ encodeResponseDefault)
/* harmony export */ });
/* harmony import */ var _esutils_invert__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @esutils/invert */ "../invert/dist/mjs/index.js");
/* harmony import */ var _reader__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./reader */ "./dist/mjs/reader.js");
/* harmony import */ var _writer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./writer */ "./dist/mjs/writer.js");
/* eslint-disable no-param-reassign */
/* eslint-disable no-continue */
/* eslint-disable no-bitwise */
/* eslint-disable max-classes-per-file */
/**
* [QUERY_TYPE description]
* @type {Object}
* @docs https://tools.ietf.org/html/rfc1035#section-3.2.2
*/
const TYPE = {
A: 0x01,
NS: 0x02,
MD: 0x03,
MF: 0x04,
CNAME: 0x05,
SOA: 0x06,
MB: 0x07,
MG: 0x08,
MR: 0x09,
NULL: 0x0a,
WKS: 0x0b,
PTR: 0x0c,
HINFO: 0x0d,
MINFO: 0x0e,
MX: 0x0f,
TXT: 0x10,
AAAA: 0x1c,
SRV: 0x21,
EDNS: 0x29,
SPF: 0x63,
AXFR: 0xfc,
MAILB: 0xfd,
MAILA: 0xfe,
ANY: 0xff,
CAA: 0x101,
};
const TYPE_INVERTED = (0,_esutils_invert__WEBPACK_IMPORTED_MODULE_0__.invert)(TYPE);
/**
* [QUERY_CLASS description]
* @type {Object}
* @docs https://tools.ietf.org/html/rfc1035#section-3.2.4
*/
const CLASS = {
IN: 0x01,
CS: 0x02,
CH: 0x03,
HS: 0x04,
ANY: 0xff,
};
/**
* [EDNS_OPTION_CODE description]
* @type {Object}
* @docs https://tools.ietf.org/html/rfc6891#section-6.1.2
*/
const EDNS_OPTION_CODE = {
ECS: 0x08,
};
/**
* [Header description]
* @param {[type]} options [description]
* @docs https://tools.ietf.org/html/rfc1035#section-4.1.1
*/
class Header {
static create() {
return {
id: 0,
qr: 0,
opcode: 0,
aa: 0,
tc: 0,
rd: 0,
ra: 0,
z: 0,
rcode: 0,
qdcount: 0,
nscount: 0,
arcount: 0,
ancount: 0,
};
}
/**
* [parse description]
* @param {[type]} buffer [description]
* @return {[type]} [description]
* @docs https://tools.ietf.org/html/rfc1035#section-4.1.1
*/
static decode(reader) {
const header = Header.create();
header.id = reader.read(16);
header.qr = reader.read(1);
header.opcode = reader.read(4);
header.aa = reader.read(1);
header.tc = reader.read(1);
header.rd = reader.read(1);
header.ra = reader.read(1);
header.z = reader.read(3);
header.rcode = reader.read(4);
header.qdcount = reader.read(16);
header.ancount = reader.read(16);
header.nscount = reader.read(16);
header.arcount = reader.read(16);
return header;
}
/**
* [toBuffer description]
* @return {[type]} [description]
*/
static encode(writer, header) {
writer.write(header.id, 16);
writer.write(header.qr, 1);
writer.write(header.opcode, 4);
writer.write(header.aa, 1);
writer.write(header.tc, 1);
writer.write(header.rd, 1);
writer.write(header.ra, 1);
writer.write(header.z, 3);
writer.write(header.rcode, 4);
writer.write(header.qdcount, 16);
writer.write(header.ancount, 16);
writer.write(header.nscount, 16);
writer.write(header.arcount, 16);
}
}
/**
* [encode_name description]
* @param {[type]} domain [description]
* @return {[type]} [description]
*/
class Name {
static COPY = 0xc0;
static decode(reader) {
const name = [];
let o;
let len = reader.read(8);
while (len) {
if ((len & Name.COPY) === Name.COPY) {
len -= Name.COPY;
len <<= 8;
const pos = len + reader.read(8);
if (!o)
o = reader.offset;
reader.offset = pos * 8;
len = reader.read(8);
continue;
}
else {
let part = '';
while (len > 0) {
len -= 1;
part += String.fromCharCode(reader.read(8));
}
name.push(part);
len = reader.read(8);
}
}
if (o)
reader.offset = o;
return name.join('.');
}
static encode(domain, writer) {
// TODO: domain name compress
(domain || '')
.split('.')
.filter((part) => !!part)
.forEach((part) => {
writer.write(part.length, 8);
part.split('').map((c) => {
writer.write(c.charCodeAt(0), 8);
return c.charCodeAt(0);
});
});
writer.write(0, 8);
}
}
/**
* Question section format
* @docs https://tools.ietf.org/html/rfc1035#section-4.1.2
*/
class Question {
static create(name, type, cls) {
return {
name: name ?? '',
type: type ?? TYPE.ANY,
class: cls ?? CLASS.ANY,
};
}
/**
* [parse description]
* @param {[type]} reader [description]
* @return {[type]} [description]
*/
static decode(reader, question) {
question.name = Name.decode(reader);
question.type = reader.read(16);
question.class = reader.read(16);
}
static encode(writer, question) {
Name.encode(question.name, writer);
writer.write(question.type, 16);
writer.write(question.class, 16);
}
}
/**
* Resource record format
* @docs https://tools.ietf.org/html/rfc1035#section-4.1.3
*/
class Resource {
/**
* [encode description]
* @param writer [description]
* @param info [description]
* @return The offset of the reserved 16 bits for resource body length
*/
static encode(writer, info) {
Name.encode(info.name, writer);
writer.write(info.type, 16);
writer.write(info.class, 16);
writer.write(info.ttl, 32);
// Reserve 16 bits for resource length
const offset = writer.buffer.length;
writer.write(0, 16);
return offset;
}
static encodeLength(writer, offset) {
const lengthInBits = writer.buffer.length - offset - 16;
writer.update(offset, lengthInBits / 8, 16);
}
/**
* [decode description]
* @param {[type]} reader [description]
* @return The length of the resource body
*/
static decode(reader, info) {
info.name = Name.decode(reader);
info.type = reader.read(16);
info.class = reader.read(16);
info.ttl = reader.read(32);
return reader.read(16);
}
}
/**
* [A description]
* @type {Object}
* @docs https://tools.ietf.org/html/rfc1035#section-3.4.1
*/
class ResourceA {
static encode(writer, info) {
const offset = Resource.encode(writer, info);
const parts = info.address.split('.');
parts.forEach((part) => {
writer.write(parseInt(part, 10), 8);
});
Resource.encodeLength(writer, offset);
}
static decode(reader, length, info) {
const parts = [];
while (length > 0) {
length -= 1;
parts.push(reader.read(8));
}
info.address = parts.join('.');
}
}
/**
* [AAAA description]
* @type {Object}
* @docs https://en.wikipedia.org/wiki/IPv6
*/
class ResourceAAAA {
static encode(writer, info) {
const offset = Resource.encode(writer, info);
const parts = info.address.split(':');
parts.forEach((part) => {
writer.write(parseInt(part, 16), 16);
});
Resource.encodeLength(writer, offset);
}
static decode(reader, length, info) {
const parts = [];
while (length) {
length -= 2;
parts.push(reader.read(16));
}
info.address = parts
.map((part) => (part > 0 ? part.toString(16) : ''))
.join(':');
}
}
/**
* [CNAME description]
* @type {Object}
* @docs https://tools.ietf.org/html/rfc1035#section-3.3.1
*/
class ResourceCNAME {
static encode(writer, info) {
const offset = Resource.encode(writer, info);
Name.encode(info.domain, writer);
Resource.encodeLength(writer, offset);
}
static decode(reader, _length, info) {
info.domain = Name.decode(reader);
}
}
/**
* [MX description]
* @param {[type]} exchange [description]
* @param {[type]} priority [description]
* @docs https://tools.ietf.org/html/rfc1035#section-3.3.9
*/
class ResourceMX {
static encode(writer, info) {
const offset = Resource.encode(writer, info);
writer.write(info.priority, 16);
Name.encode(info.exchange, writer);
Resource.encodeLength(writer, offset);
}
static decode(reader, length, info) {
info.priority = reader.read(16);
info.exchange = Name.decode(reader);
}
}
/**
* [NS description]
* @type {Object}
* @docs https://tools.ietf.org/html/rfc1035#section-3.3.11
*/
class ResourceNS {
static encode(writer, info) {
const offset = Resource.encode(writer, info);
Name.encode(info.ns, writer);
Resource.encodeLength(writer, offset);
}
static decode(reader, length, info) {
info.ns = Name.decode(reader);
}
}
class ResourceSPF {
static encode(writer, info) {
const offset = Resource.encode(writer, info);
/*
writer = writer || new Packet.Writer();
// make sure that resource data is a an array of strings
const characterStrings = Array.isArray(record.data) ? record.data : [ record.data ];
// convert array of strings to array of buffers
const characterStringBuffers = characterStrings.map(function(characterString) {
if (Buffer.isBuffer(characterString)) {
return characterString;
}
if (typeof characterString === 'string') {
return Buffer.from(characterString, 'utf8');
}
return false;
}).filter(function(characterString) {
// remove invalid values from the array
return characterString;
});
// calculate byte length of resource strings
const bufferLength = characterStringBuffers.reduce(function(sum, characterStringBuffer) {
return sum + characterStringBuffer.length;
}, 0);
// write string length to output
writer.write(bufferLength + characterStringBuffers.length, 16); // response length
// write each string to output
characterStringBuffers.forEach(function(buffer) {
writer.write(buffer.length, 8); // text length
buffer.forEach(function(c) {
writer.write(c, 8);
});
});
return writer.toBuffer();
*/
Resource.encodeLength(writer, offset);
}
static decode(_reader, _length, _info) {
/*
const parts = [];
let bytesRead = 0; let chunkLength = 0;
while (bytesRead < length) {
chunkLength = reader.read(8); // text length
bytesRead++;
while (chunkLength--) {
parts.push(reader.read(8));
bytesRead++;
}
}
this.data = Buffer.from(parts).toString('utf8');
return this;
*/
}
}
class ResourceSOA {
static encode(writer, info) {
const offset = Resource.encode(writer, info);
Name.encode(info.primary, writer);
Name.encode(info.admin, writer);
writer.write(info.serial, 32);
writer.write(info.refresh, 32);
writer.write(info.retry, 32);
writer.write(info.expiration, 32);
writer.write(info.minimum, 32);
Resource.encodeLength(writer, offset);
}
static decode(reader, _length, info) {
info.primary = Name.decode(reader);
info.admin = Name.decode(reader);
info.serial = reader.read(32);
info.refresh = reader.read(32);
info.retry = reader.read(32);
info.expiration = reader.read(32);
info.minimum = reader.read(32);
}
}
class ResourceSRV {
static encode(writer, info) {
const offset = Resource.encode(writer, info);
writer.write(info.priority, 16);
writer.write(info.weight, 16);
writer.write(info.port, 16);
Name.encode(info.target, writer);
Resource.encodeLength(writer, offset);
}
static decode(reader, length, info) {
info.priority = reader.read(16);
info.weight = reader.read(16);
info.port = reader.read(16);
info.target = Name.decode(reader);
}
}
class ResourceEDNS {
static encode(writer, info) {
const offset = Resource.encode(writer, info);
/*
const rdataWriter = new Packet.Writer();
for (const rdata of record.rdata) {
const encoder = Object.keys(Packet.EDNS_OPTION_CODE).filter(function(type) {
return rdata.ednsCode === Packet.EDNS_OPTION_CODE[type];
})[0];
if (encoder in Packet.Resource.EDNS && Packet.Resource.EDNS[encoder].encode) {
const w = new Packet.Writer();
Packet.Resource.EDNS[encoder].encode(rdata, w);
rdataWriter.write(rdata.ednsCode, 16);
rdataWriter.write(w.buffer.length / 8, 16);
rdataWriter.writeBuffer(w);
} else {
debug('node-dns > unknown EDNS rdata encoder %s(%j)', encoder, rdata.ednsCode);
}
}
writer = writer || new Packet.Writer();
writer.write(rdataWriter.buffer.length / 8, 16);
writer.writeBuffer(rdataWriter);
return writer.toBuffer();
*/
Resource.encodeLength(writer, offset);
}
static decode(_reader, _length, _info) {
/*
this.type = Packet.TYPE.EDNS;
this.class = 512;
this.ttl = 0;
this.rdata = [];
while (length) {
const optionCode = reader.read(16);
const optionLength = reader.read(16); // In octet (https://tools.ietf.org/html/rfc6891#page-8)
const decoder = Object.keys(Packet.EDNS_OPTION_CODE).filter(function(type) {
return optionCode === Packet.EDNS_OPTION_CODE[type];
})[0];
if (decoder in Packet.Resource.EDNS && Packet.Resource.EDNS[decoder].decode) {
const rdata = Packet.Resource.EDNS[decoder].decode(reader, optionLength);
this.rdata.push(rdata);
} else {
reader.read(optionLength); // Ignore data that doesn't understand
debug('node-dns > unknown EDNS rdata decoder %s(%j)', decoder, optionCode);
}
length = length - 4 - optionLength;
}
return this;
*/
}
}
function createDnsBasic() {
return {
name: '',
type: TYPE.ANY,
class: CLASS.ANY,
};
}
function decodeSingle(reader, parsed, errors, decode, count) {
for (let i = 0; i < count; i += 1) {
try {
const info = createDnsBasic();
decode(reader, info);
parsed.push(info);
}
catch (e) {
errors.push(e);
}
}
}
function encodeSingle(writer, dnsBasicList, errors, encode) {
for (let i = 0; i < dnsBasicList.length; i += 1) {
try {
encode(writer, dnsBasicList[i]);
}
catch (e) {
errors.push(e);
}
}
}
function encodeResponseDefault(writer, info) {
switch (info.type) {
case TYPE.A:
ResourceA.encode(writer, info);
break;
case TYPE.AAAA:
ResourceAAAA.encode(writer, info);
break;
case TYPE.CNAME:
case TYPE.PTR:
ResourceCNAME.encode(writer, info);
break;
default:
throw new Error(`Not supported DNS TYPE ${TYPE_INVERTED[info.type]}:${info.type}`);
}
}
function decodeResponseDefault(reader, info) {
const length = Resource.decode(reader, info);
switch (info.type) {
case TYPE.A:
ResourceA.decode(reader, length, info);
break;
case TYPE.AAAA:
ResourceAAAA.decode(reader, length, info);
break;
case TYPE.CNAME:
case TYPE.PTR:
ResourceCNAME.decode(reader, length, info);
break;
default: {
for (let lengthToRead = length; lengthToRead > 0; lengthToRead -= 1) {
reader.read(8);
}
throw new Error(`Not supported DNS TYPE ${TYPE_INVERTED[info.type]}:${info.type}`);
}
}
}
class Packet {
static create() {
return {
header: Header.create(),
errors: [],
questions: [],
answers: [],
authorities: [],
additionals: [],
};
}
/**
* [random header id]
* @return {[type]} [description]
*/
static randomHeaderId() {
return (Math.random() * 65535) | 0;
}
static decode(buffer, decoder) {
const reader = new _reader__WEBPACK_IMPORTED_MODULE_1__.BufferReader(buffer);
const dnsParsed = Packet.create();
const header = Header.decode(reader);
dnsParsed.header = header;
decodeSingle(reader, dnsParsed.questions, dnsParsed.errors, Question.decode, header.qdcount);
decodeSingle(reader, dnsParsed.answers, dnsParsed.errors, decoder, header.ancount);
decodeSingle(reader, dnsParsed.authorities, dnsParsed.errors, decoder, header.nscount);
decodeSingle(reader, dnsParsed.additionals, dnsParsed.errors, decoder, header.arcount);
return dnsParsed;
}
static encode(dnsEntry, encoder) {
const writer = new _writer__WEBPACK_IMPORTED_MODULE_2__.BufferWriter();
dnsEntry.header.qdcount = dnsEntry.questions.length;
dnsEntry.header.ancount = dnsEntry.answers.length;
dnsEntry.header.nscount = dnsEntry.authorities.length;
dnsEntry.header.arcount = dnsEntry.additionals.length;
Header.encode(writer, dnsEntry.header);
encodeSingle(writer, dnsEntry.questions, dnsEntry.errors, Question.encode);
encodeSingle(writer, dnsEntry.answers, dnsEntry.errors, encoder);
encodeSingle(writer, dnsEntry.authorities, dnsEntry.errors, encoder);
encodeSingle(writer, dnsEntry.additionals, dnsEntry.errors, encoder);
return writer.toBuffer();
}
}
//# sourceMappingURL=dns-packet.js.map
/***/ }),
/***/ "./dist/mjs/index.js":
/*!***************************!*\
!*** ./dist/mjs/index.js ***!
\***************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "BufferReader": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.BufferReader),
/* harmony export */ "BufferWriter": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.BufferWriter),
/* harmony export */ "CLASS": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.CLASS),
/* harmony export */ "EDNS_OPTION_CODE": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.EDNS_OPTION_CODE),
/* harmony export */ "Header": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.Header),
/* harmony export */ "Name": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.Name),
/* harmony export */ "Packet": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.Packet),
/* harmony export */ "Question": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.Question),
/* harmony export */ "Resource": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.Resource),
/* harmony export */ "ResourceA": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.ResourceA),
/* harmony export */ "ResourceAAAA": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.ResourceAAAA),
/* harmony export */ "ResourceCNAME": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.ResourceCNAME),
/* harmony export */ "ResourceEDNS": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.ResourceEDNS),
/* harmony export */ "ResourceMX": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.ResourceMX),
/* harmony export */ "ResourceNS": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.ResourceNS),
/* harmony export */ "ResourceSOA": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.ResourceSOA),
/* harmony export */ "ResourceSPF": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.ResourceSPF),
/* harmony export */ "ResourceSRV": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.ResourceSRV),
/* harmony export */ "TYPE": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.TYPE),
/* harmony export */ "TYPE_INVERTED": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.TYPE_INVERTED),
/* harmony export */ "createDnsBasic": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.createDnsBasic),
/* harmony export */ "decodeResponseDefault": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.decodeResponseDefault),
/* harmony export */ "encodeResponseDefault": () => (/* reexport safe */ _dns_packet__WEBPACK_IMPORTED_MODULE_0__.encodeResponseDefault)
/* harmony export */ });
/* harmony import */ var _dns_packet__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dns-packet */ "./dist/mjs/dns-packet.js");
//# sourceMappingURL=index.js.map
/***/ }),
/***/ "./dist/mjs/reader.js":
/*!****************************!*\
!*** ./dist/mjs/reader.js ***!
\****************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "BufferReader": () => (/* binding */ BufferReader)
/* harmony export */ });
/* eslint-disable no-bitwise */
function p(a) {
let n = 0;
const f = a.length - 1;
for (let i = f; i >= 0; i -= 1) {
if (a[f - i])
n += 2 ** i;
}
return n;
}
/**
* [Reader description]
* @param {[type]} buffer [description]
* @param {[type]} offset [description]
*/
class BufferReader {
buffer;
offset;
constructor(buffer, offset) {
this.buffer = buffer;
this.offset = offset || 0;
}
/**
* [read description]
* @param {[type]} buffer [description]
* @param {[type]} offset [description]
* @param {[type]} length [description]
* @return {[type]} [description]
*/
static read(buffer, offset, length) {
let a = [];
let c = Math.ceil(length / 8);
let l = Math.floor(offset / 8);
const m = offset % 8;
function t(n) {
const r = [0, 0, 0, 0, 0, 0, 0, 0];
for (let i = 7; i >= 0; i -= 1) {
r[7 - i] = n & 2 ** i ? 1 : 0;
}
a = a.concat(r);
}
const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
while (c--)
t(view.getUint8(l++));
return p(a.slice(m, m + length));
}
/**
* [read description]
* @param {[type]} size [description]
* @return {[type]} [description]
*/
read(size) {
const val = BufferReader.read(this.buffer, this.offset, size);
this.offset += size;
return val;
}
}
//# sourceMappingURL=reader.js.map
/***/ }),
/***/ "./dist/mjs/writer.js":
/*!****************************!*\
!*** ./dist/mjs/writer.js ***!
\****************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "BufferWriter": () => (/* binding */ BufferWriter)
/* harmony export */ });
/* eslint-disable no-bitwise */
/**
* [Writer description]
*/
class BufferWriter {
buffer;
constructor() {
this.buffer = [];
}
/**
* [write description]
* @param {[type]} d [description]
* @param {[type]} size [description]
* @return {[type]} [description]
*/
write(d, size) {
for (let i = 0; i < size; i += 1) {
this.buffer.push(d & (2 ** (size - i - 1)) ? 1 : 0);
}
}
update(offset, d, size) {
for (let i = 0; i < size; i += 1) {
this.buffer[offset + i] = (d & (2 ** (size - i - 1)) ? 1 : 0);
}
}
/**
* [writeBuffer description]
* @param {[type]} b [description]
*/
writeBuffer(b) {
for (let i = 0; i < b.length; i += 1) {
this.buffer.push(b[i]);
}
}
/**
* [toBuffer description]
* @return {[type]} [description]
*/
toBuffer() {
const arr = [];
for (let i = 0; i < this.buffer.length; i += 8) {
const chunk = this.buffer.slice(i, i + 8);
arr.push(parseInt(chunk.join(''), 2));
}
return new Uint8Array(arr);
}
}
//# sourceMappingURL=writer.js.map
/***/ }),
/***/ "../invert/dist/mjs/index.js":
/*!***********************************!*\
!*** ../invert/dist/mjs/index.js ***!
\***********************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "invert": () => (/* reexport safe */ _invert__WEBPACK_IMPORTED_MODULE_0__.invert)
/* harmony export */ });
/* harmony import */ var _invert__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./invert */ "../invert/dist/mjs/invert.js");
//# sourceMappingURL=index.js.map
/***/ }),
/***/ "../invert/dist/mjs/invert.js":
/*!************************************!*\
!*** ../invert/dist/mjs/invert.js ***!
\************************************/
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "invert": () => (/* binding */ invert)
/* harmony export */ });
function invert(obj) {
const retobj = {};
// eslint-disable-next-line guard-for-in, no-restricted-syntax
for (const key in obj) {
retobj[obj[key]] = key;
}
return retobj;
}
//# sourceMappingURL=invert.js.map
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ (() => {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = (module) => {
/******/ var getter = module && module.__esModule ?
/******/ () => (module['default']) :
/******/ () => (module);
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/******/ /* webpack/runtime/make namespace object */
/******/ (() => {
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/ })();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
/*!*******************************!*\
!*** ./examples/dns-proxy.ts ***!
\*******************************/
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var dgram__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! dgram */ "dgram");
/* harmony import */ var dgram__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(dgram__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! fs */ "fs");
/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _esutils_dns_packet__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @esutils/dns-packet */ "./dist/mjs/index.js");
/* harmony import */ var _dns_util__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./dns-util */ "./examples/dns-util.ts");
const DnsPort = parseInt(process.env.DNS_PORT ?? '53', 10);
function updateDomains(domains, filePath) {
const content = fs__WEBPACK_IMPORTED_MODULE_1__.readFileSync(filePath, 'utf-8');
const lines = content.split(/\r\n|\n\r|\n|\r/);
for (let i = 0; i < lines.length; i += 1) {
const line = lines[i].trim();
if (line.length > 0) {
domains[line] = 1;
}
}
}
const DnsServerListMain = [];
const DnsServerListAuxiliary = [];
const DnsServerListDefault = [];
const HelpInfo = `
--help Print the help
-?
-h
--main <file> The file path of domain list that pass through main dns server;
Can specify multiple times
--main-log <file> The file path to record main dns query, optional
--main-dns <ip> The ip of main dns server, Can specify multiple times
--auxiliary <file> The file path of domain list that pass through auxiliary dns server;
Can specify multiple times
--auxiliary-log <file> The file path to record auxiliary dns query, optional
--auxiliary-dns <ip> The ip of auxiliary dns server, Can specify multiple times
--default-dns <ip> The ip of default dns server, Can specify multiple times
`;
function parseArgs(argv) {
const mainDomains = {};
const auxiliaryDomains = {};
let mainLog;
let auxiliaryLog;
let hasHelp = false;
for (let i = 1; i < argv.length; i += 1) {
const argi = argv[i];
if (argi === '--help' || argi === '-h' || argi === '-?') {
hasHelp = true;
break;
}
else if (i < argv.length - 1) {
const argp = argv[i + 1];
if (argi === '--main') {
updateDomains(mainDomains, argp);
i += 1;
}
else if (argi === '--auxiliary') {
updateDomains(auxiliaryDomains, argp);
i += 1;
}
else if (argi === '--main-dns') {
DnsServerListMain.push({
ip: argp,
port: 53,
});
}
else if (argi === '--auxiliary-dns') {
DnsServerListAuxiliary.push({
ip: argp,
port: 53,
});
}
else if (argi === '--default-dns') {
DnsServerListDefault.push({
ip: argp,
port: 53,
});
}
else if (argi === '--main-log') {
mainLog = argp;
}
else if (argi === '--auxiliary-log') {
auxiliaryLog = argp;
}
}
}
if (hasHelp) {
console.log(HelpInfo);
process.exit(0);
}
return {
mainDomains,
mainLog,
auxiliaryDomains,
auxiliaryLog,
};
}
const domainsInfo = parseArgs(process.argv);
function checkDomains(domains, domainItems) {
for (let i = domainItems.length - 1; i >= 0; i -= 1) {
const subDomain = domainItems.slice(i).join('.');
// Support matching both python.org and .python.org
if (Object.hasOwn(domains, subDomain) || Object.hasOwn(domains, `.${subDomain}`)) {
return true;
}
}
return false;
}
function getDnsList(domain) {
const domainItems = domain.split('.');
// main domain have higher priority
if (checkDomains(domainsInfo.mainDomains, domainItems)) {
return DnsServerListMain;
}
if (checkDomains(domainsInfo.auxiliaryDomains, domainItems)) {
return DnsServerListAuxiliary;
}
return DnsServerListDefault;
}
async function startDnsServer() {
const server = dgram__WEBPACK_IMPORTED_MODULE_0__.createSocket('udp4');
// emits when any error occurs
server.on('error', (error) => {
console.log(`Error: ${error}`);
server.close();
});
// emits on new datagram msg
let mainLogFile;
if (domainsInfo.mainLog) {
mainLogFile = await fs__WEBPACK_IMPORTED_MODULE_1__.promises.open(domainsInfo.mainLog, 'a');
}
let auxiliaryLogFile;
if (domainsInfo.auxiliaryLog) {
auxiliaryLogFile = await fs__WEBPACK_IMPORTED_MODULE_1__.promises.open(domainsInfo.auxiliaryLog, 'a');
}
server.on('message', async (message, rinfo) => {
const request = _esutils_dns_packet__WEBPACK_IMPORTED_MODULE_2__.Packet.decode(message, _esutils_dns_packet__WEBPACK_IMPORTED_MODULE_2__.decodeResponseDefault);
const questions = request.questions.filter((x) => x.type === _esutils_dns_packet__WEBPACK_IMPORTED_MODULE_2__.TYPE.A || x.type === _esutils_dns_packet__WEBPACK_IMPORTED_MODULE_2__.TYPE.AAAA || x.type === _esutils_dns_packet__WEBPACK_IMPORTED_MODULE_2__.TYPE.CNAME);
const response = {
header: request.header,
errors: [],
questions: request.questions,
answers: [],
authorities: [],
additionals: [],
};
if (questions.length === 1) {
try {
const { name } = questions[0];
const dnsList = getDnsList(name);
const parallelResponse = await (0,_dns_util__WEBPACK_IMPORTED_MODULE_3__.queryMultipleDNS)(dnsList, questions);
response.header = parallelResponse.packet.header;
response.header.id = request.header.id;
response.questions = parallelResponse.packet.questions;
response.answers = parallelResponse.packet.answers;
response.authorities = parallelResponse.packet.authorities;
response.additionals = parallelResponse.packet.additionals;
for (let i = 0; i < response.answers.length; i += 1) {
const answer = response.answers[i];
if (answer.type === _esutils_dns_packet__WEBPACK_IMPORTED_MODULE_2__.TYPE.A || answer.type === _esutils_dns_packet__WEBPACK_IMPORTED_MODULE_2__.TYPE.AAAA) {
const answerIp = answer;
const logItem = `${name} ${answerIp.address}\n`;
if (mainLogFile && dnsList === DnsServerListMain) {
mainLogFile.write(logItem);
}
if (auxiliaryLogFile && dnsList === DnsServerListAuxiliary) {
auxiliaryLogFile.write(logItem);
}
}
}
}
catch (error) {
if (error instanceof AggregateError) {
console.log(error.errors);
}
}
}
// console.log(`The naswer is: ${JSON.stringify(response.answers)}`);
const responseBuffer = _esutils_dns_packet__WEBPACK_IMPORTED_MODULE_2__.Packet.encode(response, _esutils_dns_packet__WEBPACK_IMPORTED_MODULE_2__.encodeResponseDefault);
server.send(responseBuffer, rinfo.port, rinfo.address);
});
// emits when socket is ready and listening for datagram msgs
server.on('listening', () => {
const address = server.address();
const { port } = address;
const { family } = address;
const ipaddr = address.address;
console.log(`Server is listening at port: ${port}`);
console.log(`Server ip: ${ipaddr}`);
console.log(`Server is IP4/IP6: ${family}`);
});
// emits after the socket is closed using socket.close();
server.on('close', () => {
if (mainLogFile) {
mainLogFile.close();
}
if (auxiliaryLogFile) {
auxiliaryLogFile.close();
}
console.log('Socket is closed !');
});
server.bind(DnsPort);
}
startDnsServer();
})();
/******/ })()
;
//# sourceMappingURL=dns-proxy.js.map