UNPKG

@esutils/dns-packet

Version:

A minimal dns-packet library that implemented in `typescript`

1,244 lines (1,186 loc) 45.4 kB
/******/ (() => { // 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