2021-05-15 16:29:38 +00:00
|
|
|
import { CachedZone, SOARecord } from '../models/interfaces';
|
|
|
|
import { readZoneFile } from './reader';
|
|
|
|
import { DNSRecordType } from './records';
|
|
|
|
import { ReloadExecutor } from './rndc';
|
|
|
|
import { ValidatorExecutor } from './validator';
|
2021-05-15 08:45:12 +00:00
|
|
|
|
|
|
|
export class DNSCache {
|
|
|
|
private cached: Record<string, CachedZone> = {};
|
|
|
|
|
2021-05-15 16:29:38 +00:00
|
|
|
constructor(
|
|
|
|
private rndc: ReloadExecutor,
|
|
|
|
private validator: ValidatorExecutor,
|
|
|
|
private ttl = 1600
|
|
|
|
) {}
|
2021-05-15 08:45:12 +00:00
|
|
|
|
|
|
|
has(name: string): boolean {
|
|
|
|
return this.cached[name] != null;
|
|
|
|
}
|
|
|
|
|
|
|
|
async get(name: string): Promise<CachedZone | null> {
|
|
|
|
const cached = this.cached[name];
|
|
|
|
if (!cached) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cached.changed.getTime() < new Date().getTime() - this.ttl * 1000) {
|
|
|
|
return this.load(name, cached.file);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.cached[name];
|
|
|
|
}
|
|
|
|
|
|
|
|
async set(name: string, zone: CachedZone): Promise<void> {
|
|
|
|
this.cached[name] = zone;
|
|
|
|
}
|
|
|
|
|
|
|
|
async load(name: string, file: string): Promise<CachedZone> {
|
|
|
|
const zoneFile = await readZoneFile(file);
|
|
|
|
const cache = {
|
|
|
|
name,
|
|
|
|
file,
|
|
|
|
zone: zoneFile,
|
|
|
|
added: new Date(),
|
|
|
|
changed: new Date()
|
|
|
|
}
|
|
|
|
this.cached[name] = cache;
|
|
|
|
return cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
async save(name: string): Promise<void> {
|
|
|
|
const zone = await this.get(name);
|
|
|
|
if (!zone) {
|
|
|
|
throw new Error('No such cached zone file!');
|
|
|
|
}
|
2021-05-15 16:29:38 +00:00
|
|
|
|
|
|
|
await this.validator.validateAndSave(name, zone);
|
2021-05-15 08:45:12 +00:00
|
|
|
}
|
|
|
|
|
2021-05-15 16:29:38 +00:00
|
|
|
async update(name: string, newZone?: CachedZone, skipReload = false): Promise<void> {
|
2021-05-15 08:45:12 +00:00
|
|
|
let zone: CachedZone | null;
|
|
|
|
if (newZone) {
|
|
|
|
zone = newZone;
|
|
|
|
} else {
|
|
|
|
zone = await this.get(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!zone) {
|
|
|
|
throw new Error('No such cached zone file!');
|
|
|
|
}
|
|
|
|
|
|
|
|
zone.changed = new Date();
|
|
|
|
const soa = zone.zone.records.find((record) => record.type === DNSRecordType.SOA) as SOARecord;
|
|
|
|
soa.serial = Math.floor(Date.now() / 1000);
|
|
|
|
|
|
|
|
this.set(name, zone);
|
2021-05-15 16:29:38 +00:00
|
|
|
await this.save(name);
|
|
|
|
|
|
|
|
if (!skipReload) {
|
|
|
|
try {
|
|
|
|
await this.rndc.reload(name);
|
|
|
|
} catch (e) {
|
|
|
|
console.warn('%s automatic zone reload failed:', name, e.stack);
|
|
|
|
}
|
|
|
|
}
|
2021-05-15 08:45:12 +00:00
|
|
|
}
|
|
|
|
}
|