parent
5bf7af6565
commit
07e2dff29f
@ -1,6 +1,6 @@
|
||||
import { Request, parseInput } from "./request";
|
||||
import { RDATARecord, RecourceRecord } from "./record";
|
||||
import { MonitoringPlugin, QuestionPlugin, DnsCore, AnswerHandler, ListenerPlugin, StoragePlugin, Record } from ".";
|
||||
import { MonitoringPlugin, QuestionPlugin, DnsCore, AnswerHandler, ListenerPlugin, StoragePlugin, Record } from "./core";
|
||||
import assert = require("assert");
|
||||
import { RecordTypes } from "./types";
|
||||
import { Question } from "./question";
|
||||
|
309
src/core.ts
Normal file
309
src/core.ts
Normal file
@ -0,0 +1,309 @@
|
||||
import { Request } from "./request";
|
||||
import { RecordTypes } from "./types";
|
||||
import * as LRU from "lru-cache";
|
||||
import { Writable } from "stream";
|
||||
import { Question } from "./question";
|
||||
import { RecourceRecord } from "./record";
|
||||
|
||||
// export class AnswerSteam extends Writable {
|
||||
|
||||
// }
|
||||
|
||||
export interface Record {
|
||||
type: RecordTypes
|
||||
domain: string
|
||||
hostname: string
|
||||
value: string
|
||||
ttl: number
|
||||
priority?: number
|
||||
additional?: string
|
||||
}
|
||||
|
||||
export interface Plugin {
|
||||
/**
|
||||
* This sets the priority of this Plugin.
|
||||
*
|
||||
* The lower the value, the higher priority.
|
||||
* It is recommended to make this value changeable over the cosntructor options field.
|
||||
*/
|
||||
Priority: number
|
||||
|
||||
init(core: DnsCore): Promise<void>
|
||||
}
|
||||
|
||||
export interface ListenerPlugin extends Plugin {
|
||||
/**
|
||||
* This method is for registering a callback that is called
|
||||
* when packet is received and ready for parsing.
|
||||
* The function returns the Answer data. Errors shouldn't be possible
|
||||
* by this function
|
||||
*/
|
||||
registerCallback(callback: (data: Buffer, sender: string, max_size?: number) => Promise<Buffer>): void
|
||||
registerCallback(callback: (data: Buffer, sender: string, answer: Writable) => Promise<void>)
|
||||
}
|
||||
|
||||
export class ListenerManager {
|
||||
private _listener: ListenerPlugin[] = []
|
||||
|
||||
add(listener: ListenerPlugin) {
|
||||
this._listener.push(listener)
|
||||
}
|
||||
|
||||
setup(core: DnsCore) {
|
||||
return Promise.all(this._listener.map(e => e.init(core)))
|
||||
}
|
||||
|
||||
registerCallback(callback: (data: Buffer, sender: string, max_size?: number) => Promise<Buffer>) {
|
||||
this._listener.forEach(e => e.registerCallback(callback))
|
||||
}
|
||||
}
|
||||
|
||||
export interface MonitoringPlugin extends Plugin {
|
||||
onRequest(domain: string, hostname: string, type: RecordTypes, additionalInformations): void
|
||||
}
|
||||
|
||||
export class MonitoringManager {
|
||||
private _monitoring: MonitoringPlugin[] = []
|
||||
|
||||
add(monitoring: MonitoringPlugin) {
|
||||
this._monitoring.push(monitoring)
|
||||
}
|
||||
|
||||
setup(core: DnsCore) {
|
||||
return Promise.all(this._monitoring.map(e => e.init(core)))
|
||||
}
|
||||
|
||||
onRequest(domain: string, hostname: string, type: RecordTypes, additionalInformations) {
|
||||
this._monitoring.forEach(e => e.onRequest(domain, hostname, type, additionalInformations))
|
||||
}
|
||||
}
|
||||
|
||||
export interface StoragePlugin extends Plugin {
|
||||
/**
|
||||
* Here you can define wich record types this storage plugin is capable of.
|
||||
*/
|
||||
RecordTypes: RecordTypes[]
|
||||
|
||||
/**
|
||||
* Disables record caching for this Storage
|
||||
*/
|
||||
NoCache: boolean;
|
||||
|
||||
/**
|
||||
* Returns if storage plugin is responsible for this domain.
|
||||
* This check is synchroneus because it should be quick.
|
||||
*
|
||||
* @param domain The domain for check
|
||||
*/
|
||||
isResponsible(domain: string): boolean
|
||||
getRecords(domain: string, hostname: string, type: RecordTypes): Promise<Record[] | undefined>
|
||||
getAllRecordsForDomain(domain: string): Promise<Record[]>
|
||||
}
|
||||
|
||||
type SortedStorage = {
|
||||
[P in keyof any]: StoragePlugin[] | undefined
|
||||
}
|
||||
|
||||
const cacheoptions: LRU.Options = {
|
||||
length: () => 1,
|
||||
max: 500,
|
||||
maxAge: 10 * 60 * 1000
|
||||
}
|
||||
|
||||
class RecordCache {
|
||||
private cache = new LRU<string, Record[]>(cacheoptions);
|
||||
|
||||
getRecords(domain: string, hostname: string, type: RecordTypes) {
|
||||
let key = `${domain};;${hostname};;${RecordTypes[type]}`;
|
||||
let rec = this.cache.get(key)
|
||||
if (!rec) {
|
||||
return undefined;
|
||||
}
|
||||
return rec;
|
||||
}
|
||||
|
||||
addRecords(domain: string, hostname: string, type: RecordTypes, records: Record[], ttl: number) {
|
||||
let key = `${domain};;${hostname};;${RecordTypes[type]}`;
|
||||
this.cache.set(key, records, ttl);
|
||||
}
|
||||
}
|
||||
|
||||
export class StorageManager {
|
||||
private _storages: StoragePlugin[] = []
|
||||
private _sorted: SortedStorage;
|
||||
private _cache = new RecordCache();
|
||||
|
||||
add(storage: StoragePlugin) {
|
||||
this._storages.push(storage)
|
||||
}
|
||||
|
||||
private presort() {
|
||||
this._storages = this._storages.sort((e1, e2) => e1.Priority - e2.Priority)
|
||||
this._sorted = <any>{}
|
||||
for (let key in RecordTypes) {
|
||||
let key_n = Number(key)
|
||||
if (key_n !== Number.NaN) {
|
||||
//The _storages list is sorted with priority.
|
||||
this._sorted[key_n] = this._storages.filter(e => e.RecordTypes.find(t => t === key_n) !== undefined)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setup(core: DnsCore) {
|
||||
this.presort()
|
||||
return Promise.all(this._storages.map(e => e.init(core)))
|
||||
}
|
||||
|
||||
async getAllRecordsForDomain(domain: string) {
|
||||
return this._storages.find(e => e.isResponsible(domain)).getAllRecordsForDomain(domain);
|
||||
}
|
||||
|
||||
async getRecords(domain: string, hostname: string, type: RecordTypes) {
|
||||
// First check if record is cached. Since the dns entries should not chacnge quickly
|
||||
// using the cach for quicker response times is the way to go.
|
||||
let records = this._cache.getRecords(domain, hostname, type);
|
||||
if (records) return records;
|
||||
|
||||
let storages = this._sorted[type].filter(s => s.isResponsible(domain));
|
||||
if (!storages || storages.length <= 0) return undefined
|
||||
|
||||
let nocache = false;
|
||||
for (let storage of storages) {
|
||||
nocache = storage.NoCache || nocache;
|
||||
records = await storage.getRecords(domain, hostname, type)
|
||||
if (records) {
|
||||
if (storage.NoCache) this._cache.addRecords(domain, hostname, type, records, records.length <= 0 ? 3600 : records[0].ttl)
|
||||
return records;
|
||||
}
|
||||
}
|
||||
|
||||
// If any of the checked storage engines that is
|
||||
// responsible for the domain has the noCache flag,
|
||||
// caching will be disabled for all.
|
||||
if (!nocache)
|
||||
this._cache.addRecords(domain, hostname, type, [], 0);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export interface AnswerHandler {
|
||||
addQuestion: (question: Question) => void;
|
||||
addAnswer: (record: RecourceRecord) => void;
|
||||
addAdditional: (record: RecourceRecord) => void;
|
||||
addAutority: (record: RecourceRecord) => void
|
||||
}
|
||||
|
||||
export interface QuestionPlugin extends Plugin {
|
||||
/**
|
||||
* This field defines wich question types this plugin is capable of handling.
|
||||
*/
|
||||
QuestionTypes: RecordTypes[];
|
||||
init(core: DnsCore): Promise<void>
|
||||
|
||||
/**
|
||||
*
|
||||
* @param question The question this plugin should solve
|
||||
* @param request This gives the plugin access to relevant
|
||||
* functions for queing depending questions or adding answers etc.
|
||||
* @param next This function should be called if the plugin is not able to resolve
|
||||
* the request. This will trigger other handlers.
|
||||
*/
|
||||
handleQuestion(question: Question, request: AnswerHandler, next: () => void): Promise<void>
|
||||
}
|
||||
|
||||
export class QuestionManager {
|
||||
private _questions: QuestionPlugin[] = [];
|
||||
private _sorted = <any>{};
|
||||
add(question: QuestionPlugin) {
|
||||
this._questions.push(question);
|
||||
}
|
||||
|
||||
private sort() {
|
||||
this._questions = this._questions.sort((e1, e2) => e1.Priority - e2.Priority);
|
||||
this._sorted = {};
|
||||
for (let key in RecordTypes) {
|
||||
let key_n = Number(key)
|
||||
if (key_n !== Number.NaN) {
|
||||
this._sorted[key_n] = this._questions.filter(e => e.QuestionTypes.find(t => t === key_n) !== undefined)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setup(core: DnsCore) {
|
||||
this.sort();
|
||||
return Promise.all(this._questions.map(e => e.init(core)))
|
||||
}
|
||||
|
||||
async handleQuestion(question: Question, request: AnswerHandler) {
|
||||
let handlers: QuestionPlugin[] = this._sorted[question.QTYPE];
|
||||
if (!handlers || handlers.length <= 0) return;
|
||||
let index = 0;
|
||||
while (index < handlers.length) {
|
||||
let i = index;
|
||||
await handlers[i].handleQuestion(question, request, () => {
|
||||
index++;
|
||||
})
|
||||
if (i === index) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class DnsCore {
|
||||
storageManager: StorageManager;
|
||||
addStorage(plugin: StoragePlugin) {
|
||||
this.storageManager.add(plugin);
|
||||
}
|
||||
monitoringManager: MonitoringManager;
|
||||
addMonitoring(plugin: MonitoringPlugin) {
|
||||
this.monitoringManager.add(plugin);
|
||||
}
|
||||
listenerManager: ListenerManager;
|
||||
addListener(plugin: ListenerPlugin) {
|
||||
this.listenerManager.add(plugin);
|
||||
}
|
||||
|
||||
questionManager: QuestionManager;
|
||||
addQuestion(plugin: QuestionPlugin) {
|
||||
this.questionManager.add(plugin);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.listenerManager = new ListenerManager()
|
||||
this.monitoringManager = new MonitoringManager()
|
||||
this.storageManager = new StorageManager()
|
||||
this.questionManager = new QuestionManager()
|
||||
}
|
||||
|
||||
async start() {
|
||||
await this.storageManager.setup(this);
|
||||
await this.monitoringManager.setup(this);
|
||||
await this.listenerManager.setup(this);
|
||||
await this.questionManager.setup(this);
|
||||
this.listenerManager.registerCallback(this.onMessage.bind(this));
|
||||
}
|
||||
|
||||
private async onMessage(data: Buffer, sender: string, max_size: number = 0) {
|
||||
let request = new Request(data, sender, max_size);
|
||||
let questionqueue: Promise<void>[] = [];
|
||||
let handleQuestion = question => {
|
||||
let prom = this.questionManager.handleQuestion(question, {
|
||||
addAdditional: record => {
|
||||
request.addAdditionals(record)
|
||||
},
|
||||
addAnswer: record => {
|
||||
request.addAnswer(record);
|
||||
},
|
||||
addAutority: record => {
|
||||
request.addAuthorities(record);
|
||||
},
|
||||
addQuestion: question => {
|
||||
handleQuestion(question)
|
||||
}
|
||||
})
|
||||
questionqueue.push(prom);
|
||||
}
|
||||
request.questions.map(q => handleQuestion(q));
|
||||
await Promise.all(questionqueue);
|
||||
return request.serialize();
|
||||
}
|
||||
}
|
313
src/index.ts
313
src/index.ts
@ -1,309 +1,4 @@
|
||||
import { Request } from "./request";
|
||||
import { RecordTypes } from "./types";
|
||||
import * as LRU from "lru-cache";
|
||||
import { Writable } from "stream";
|
||||
import { Question } from "./question";
|
||||
import { RecourceRecord } from "./record";
|
||||
|
||||
// export class AnswerSteam extends Writable {
|
||||
|
||||
// }
|
||||
|
||||
export interface Record {
|
||||
type: RecordTypes
|
||||
domain: string
|
||||
hostname: string
|
||||
value: string
|
||||
ttl: number
|
||||
priority?: number
|
||||
additional?: string
|
||||
}
|
||||
|
||||
export interface Plugin {
|
||||
/**
|
||||
* This sets the priority of this Plugin.
|
||||
*
|
||||
* The lower the value, the higher priority.
|
||||
* It is recommended to make this value changeable over the cosntructor options field.
|
||||
*/
|
||||
Priority: number
|
||||
|
||||
init(core: DnsCore): Promise<void>
|
||||
}
|
||||
|
||||
export interface ListenerPlugin extends Plugin {
|
||||
/**
|
||||
* This method is for registering a callback that is called
|
||||
* when packet is received and ready for parsing.
|
||||
* The function returns the Answer data. Errors shouldn't be possible
|
||||
* by this function
|
||||
*/
|
||||
registerCallback(callback: (data: Buffer, sender: string, max_size?: number) => Promise<Buffer>): void
|
||||
registerCallback(callback: (data: Buffer, sender: string, answer: Writable) => Promise<void>)
|
||||
}
|
||||
|
||||
export class ListenerManager {
|
||||
private _listener: ListenerPlugin[] = []
|
||||
|
||||
add(listener: ListenerPlugin) {
|
||||
this._listener.push(listener)
|
||||
}
|
||||
|
||||
setup(core: DnsCore) {
|
||||
return Promise.all(this._listener.map(e => e.init(core)))
|
||||
}
|
||||
|
||||
registerCallback(callback: (data: Buffer, sender: string, max_size?: number) => Promise<Buffer>) {
|
||||
this._listener.forEach(e => e.registerCallback(callback))
|
||||
}
|
||||
}
|
||||
|
||||
export interface MonitoringPlugin extends Plugin {
|
||||
onRequest(domain: string, hostname: string, type: RecordTypes, additionalInformations): void
|
||||
}
|
||||
|
||||
export class MonitoringManager {
|
||||
private _monitoring: MonitoringPlugin[] = []
|
||||
|
||||
add(monitoring: MonitoringPlugin) {
|
||||
this._monitoring.push(monitoring)
|
||||
}
|
||||
|
||||
setup(core: DnsCore) {
|
||||
return Promise.all(this._monitoring.map(e => e.init(core)))
|
||||
}
|
||||
|
||||
onRequest(domain: string, hostname: string, type: RecordTypes, additionalInformations) {
|
||||
this._monitoring.forEach(e => e.onRequest(domain, hostname, type, additionalInformations))
|
||||
}
|
||||
}
|
||||
|
||||
export interface StoragePlugin extends Plugin {
|
||||
/**
|
||||
* Here you can define wich record types this storage plugin is capable of.
|
||||
*/
|
||||
RecordTypes: RecordTypes[]
|
||||
|
||||
/**
|
||||
* Disables record caching for this Storage
|
||||
*/
|
||||
NoCache: boolean;
|
||||
|
||||
/**
|
||||
* Returns if storage plugin is responsible for this domain.
|
||||
* This check is synchroneus because it should be quick.
|
||||
*
|
||||
* @param domain The domain for check
|
||||
*/
|
||||
isResponsible(domain: string): boolean
|
||||
getRecords(domain: string, hostname: string, type: RecordTypes): Promise<Record[] | undefined>
|
||||
getAllRecordsForDomain(domain: string): Promise<Record[]>
|
||||
}
|
||||
|
||||
type SortedStorage = {
|
||||
[P in keyof any]: StoragePlugin[] | undefined
|
||||
}
|
||||
|
||||
const cacheoptions: LRU.Options = {
|
||||
length: () => 1,
|
||||
max: 500,
|
||||
maxAge: 10 * 60 * 1000
|
||||
}
|
||||
|
||||
class RecordCache {
|
||||
private cache = new LRU<string, Record[]>(cacheoptions);
|
||||
|
||||
getRecords(domain: string, hostname: string, type: RecordTypes) {
|
||||
let key = `${domain};;${hostname};;${RecordTypes[type]}`;
|
||||
let rec = this.cache.get(key)
|
||||
if (!rec) {
|
||||
return undefined;
|
||||
}
|
||||
return rec;
|
||||
}
|
||||
|
||||
addRecords(domain: string, hostname: string, type: RecordTypes, records: Record[], ttl: number) {
|
||||
let key = `${domain};;${hostname};;${RecordTypes[type]}`;
|
||||
this.cache.set(key, records, ttl);
|
||||
}
|
||||
}
|
||||
|
||||
export class StorageManager {
|
||||
private _storages: StoragePlugin[] = []
|
||||
private _sorted: SortedStorage;
|
||||
private _cache = new RecordCache();
|
||||
|
||||
add(storage: StoragePlugin) {
|
||||
this._storages.push(storage)
|
||||
}
|
||||
|
||||
private presort() {
|
||||
this._storages = this._storages.sort((e1, e2) => e1.Priority - e2.Priority)
|
||||
this._sorted = <any>{}
|
||||
for (let key in RecordTypes) {
|
||||
let key_n = Number(key)
|
||||
if (key_n !== Number.NaN) {
|
||||
//The _storages list is sorted with priority.
|
||||
this._sorted[key_n] = this._storages.filter(e => e.RecordTypes.find(t => t === key_n) !== undefined)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setup(core: DnsCore) {
|
||||
this.presort()
|
||||
return Promise.all(this._storages.map(e => e.init(core)))
|
||||
}
|
||||
|
||||
async getAllRecordsForDomain(domain: string) {
|
||||
return this._storages.find(e => e.isResponsible(domain)).getAllRecordsForDomain(domain);
|
||||
}
|
||||
|
||||
async getRecords(domain: string, hostname: string, type: RecordTypes) {
|
||||
// First check if record is cached. Since the dns entries should not chacnge quickly
|
||||
// using the cach for quicker response times is the way to go.
|
||||
let records = this._cache.getRecords(domain, hostname, type);
|
||||
if (records) return records;
|
||||
|
||||
let storages = this._sorted[type].filter(s => s.isResponsible(domain));
|
||||
if (!storages || storages.length <= 0) return undefined
|
||||
|
||||
let nocache = false;
|
||||
for (let storage of storages) {
|
||||
nocache = storage.NoCache || nocache;
|
||||
records = await storage.getRecords(domain, hostname, type)
|
||||
if (records) {
|
||||
if (storage.NoCache) this._cache.addRecords(domain, hostname, type, records, records.length <= 0 ? 3600 : records[0].ttl)
|
||||
return records;
|
||||
}
|
||||
}
|
||||
|
||||
// If any of the checked storage engines that is
|
||||
// responsible for the domain has the noCache flag,
|
||||
// caching will be disabled for all.
|
||||
if (!nocache)
|
||||
this._cache.addRecords(domain, hostname, type, [], 0);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export interface AnswerHandler {
|
||||
addQuestion: (question: Question) => void;
|
||||
addAnswer: (record: RecourceRecord) => void;
|
||||
addAdditional: (record: RecourceRecord) => void;
|
||||
addAutority: (record: RecourceRecord) => void
|
||||
}
|
||||
|
||||
export interface QuestionPlugin extends Plugin {
|
||||
/**
|
||||
* This field defines wich question types this plugin is capable of handling.
|
||||
*/
|
||||
QuestionTypes: RecordTypes[];
|
||||
init(core: DnsCore): Promise<void>
|
||||
|
||||
/**
|
||||
*
|
||||
* @param question The question this plugin should solve
|
||||
* @param request This gives the plugin access to relevant
|
||||
* functions for queing depending questions or adding answers etc.
|
||||
* @param next This function should be called if the plugin is not able to resolve
|
||||
* the request. This will trigger other handlers.
|
||||
*/
|
||||
handleQuestion(question: Question, request: AnswerHandler, next: () => void): Promise<void>
|
||||
}
|
||||
|
||||
export class QuestionManager {
|
||||
private _questions: QuestionPlugin[] = [];
|
||||
private _sorted = <any>{};
|
||||
add(question: QuestionPlugin) {
|
||||
this._questions.push(question);
|
||||
}
|
||||
|
||||
private sort() {
|
||||
this._questions = this._questions.sort((e1, e2) => e1.Priority - e2.Priority);
|
||||
this._sorted = {};
|
||||
for (let key in RecordTypes) {
|
||||
let key_n = Number(key)
|
||||
if (key_n !== Number.NaN) {
|
||||
this._sorted[key_n] = this._questions.filter(e => e.QuestionTypes.find(t => t === key_n) !== undefined)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setup(core: DnsCore) {
|
||||
this.sort();
|
||||
return Promise.all(this._questions.map(e => e.init(core)))
|
||||
}
|
||||
|
||||
async handleQuestion(question: Question, request: AnswerHandler) {
|
||||
let handlers: QuestionPlugin[] = this._sorted[question.QTYPE];
|
||||
if (!handlers || handlers.length <= 0) return;
|
||||
let index = 0;
|
||||
while (index < handlers.length) {
|
||||
let i = index;
|
||||
await handlers[i].handleQuestion(question, request, () => {
|
||||
index++;
|
||||
})
|
||||
if (i === index) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class DnsCore {
|
||||
storageManager: StorageManager;
|
||||
addStorage(plugin: StoragePlugin) {
|
||||
this.storageManager.add(plugin);
|
||||
}
|
||||
monitoringManager: MonitoringManager;
|
||||
addMonitoring(plugin: MonitoringPlugin) {
|
||||
this.monitoringManager.add(plugin);
|
||||
}
|
||||
listenerManager: ListenerManager;
|
||||
addListener(plugin: ListenerPlugin) {
|
||||
this.listenerManager.add(plugin);
|
||||
}
|
||||
|
||||
questionManager: QuestionManager;
|
||||
addQuestion(plugin: QuestionPlugin) {
|
||||
this.questionManager.add(plugin);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.listenerManager = new ListenerManager()
|
||||
this.monitoringManager = new MonitoringManager()
|
||||
this.storageManager = new StorageManager()
|
||||
this.questionManager = new QuestionManager()
|
||||
}
|
||||
|
||||
async start() {
|
||||
await this.storageManager.setup(this);
|
||||
await this.monitoringManager.setup(this);
|
||||
await this.listenerManager.setup(this);
|
||||
await this.questionManager.setup(this);
|
||||
this.listenerManager.registerCallback(this.onMessage.bind(this));
|
||||
}
|
||||
|
||||
private async onMessage(data: Buffer, sender: string, max_size: number = 0) {
|
||||
let request = new Request(data, sender, max_size);
|
||||
let questionqueue: Promise<void>[] = [];
|
||||
let handleQuestion = question => {
|
||||
let prom = this.questionManager.handleQuestion(question, {
|
||||
addAdditional: record => {
|
||||
request.addAdditionals(record)
|
||||
},
|
||||
addAnswer: record => {
|
||||
request.addAnswer(record);
|
||||
},
|
||||
addAutority: record => {
|
||||
request.addAuthorities(record);
|
||||
},
|
||||
addQuestion: question => {
|
||||
handleQuestion(question)
|
||||
}
|
||||
})
|
||||
questionqueue.push(prom);
|
||||
}
|
||||
request.questions.map(q => handleQuestion(q));
|
||||
await Promise.all(questionqueue);
|
||||
return request.serialize();
|
||||
}
|
||||
}
|
||||
export { DnsCore, ListenerPlugin, MonitoringPlugin, StoragePlugin, QuestionPlugin, AnswerHandler, Record } from "./core"
|
||||
export { RecourceRecord, RDATARecord } from "./record"
|
||||
export { Request } from "./request"
|
||||
export { Label } from "./label"
|
@ -338,7 +338,7 @@ describe("parser", function () {
|
||||
})
|
||||
})
|
||||
|
||||
import { DnsCore, StoragePlugin, Record, MonitoringPlugin, QuestionPlugin, AnswerHandler, ListenerPlugin } from "./index"
|
||||
import { DnsCore, StoragePlugin, Record, MonitoringPlugin, QuestionPlugin, AnswerHandler, ListenerPlugin } from "./core"
|
||||
|
||||
describe("DNS Core", function () {
|
||||
it("Initialization", () => {
|
||||
|
Loading…
Reference in New Issue
Block a user