103 lines
2.5 KiB
TypeScript
103 lines
2.5 KiB
TypeScript
import * as fs from 'fs/promises';
|
|
import { DNSRecordType } from 'src/types/dns.enum';
|
|
import { DNSRecord, SOARecord, DNSZone } from 'src/types/dns.interfaces';
|
|
|
|
function cleanString(str: string): string {
|
|
return str
|
|
.replace(/;[^"]+$/, '') // Remove comments from the end
|
|
.replace(/\s+/g, ' ') // Remove duplicate whitespace
|
|
.trim(); // Remove whitespace from start and end
|
|
}
|
|
|
|
function parseRecordLine(
|
|
line: string,
|
|
index: number,
|
|
lines: string[],
|
|
): DNSRecord | SOARecord | null {
|
|
let actualLine = '';
|
|
const clean = cleanString(line);
|
|
if (clean.includes('(')) {
|
|
actualLine += clean;
|
|
const trimLines = lines.slice(index + 1);
|
|
for (const trimLine of trimLines) {
|
|
actualLine += ' ' + cleanString(trimLine);
|
|
if (trimLine.includes(')')) {
|
|
break;
|
|
}
|
|
}
|
|
actualLine = cleanString(actualLine.replace(/\(|\)/g, ''));
|
|
} else {
|
|
actualLine = clean;
|
|
}
|
|
|
|
const split = actualLine.replace(/"\s"/g, '').split(' ');
|
|
if (split[0] === 'IN' && split[1] === 'NS') {
|
|
return {
|
|
name: '',
|
|
type: DNSRecordType.NS,
|
|
value: split.slice(2).join(' '),
|
|
};
|
|
}
|
|
|
|
if (split[2] === 'SOA') {
|
|
return {
|
|
name: split[0],
|
|
type: DNSRecordType.SOA,
|
|
value: split.slice(3).join(' '),
|
|
nameserver: split[3],
|
|
email: split[4],
|
|
serial: parseInt(split[5], 10),
|
|
refresh: parseInt(split[6], 10),
|
|
retry: parseInt(split[7], 10),
|
|
expire: parseInt(split[8], 10),
|
|
minimum: parseInt(split[9], 10),
|
|
};
|
|
}
|
|
|
|
if (!actualLine.includes('IN')) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
name: split[0],
|
|
type: DNSRecordType[<keyof typeof DNSRecordType>split[2]],
|
|
value: split.slice(3).join(' '),
|
|
};
|
|
}
|
|
|
|
export function parseZoneFile(lines: string[]): DNSZone {
|
|
let ttl = 0;
|
|
const includes = [];
|
|
const records: DNSRecord[] = [];
|
|
|
|
for (const index in lines) {
|
|
const line = lines[index];
|
|
if (line.startsWith('$TTL')) {
|
|
ttl = parseInt(line.split(' ')[1], 10);
|
|
continue;
|
|
}
|
|
|
|
if (line.startsWith('$INCLUDE')) {
|
|
includes.push(...line.split(' ').slice(1));
|
|
continue;
|
|
}
|
|
|
|
const record = parseRecordLine(line, parseInt(index, 10), lines);
|
|
if (record) {
|
|
records.push(record);
|
|
}
|
|
}
|
|
|
|
return {
|
|
ttl,
|
|
includes,
|
|
records,
|
|
};
|
|
}
|
|
|
|
export async function readZoneFile(file: string): Promise<DNSZone> {
|
|
const lines = await fs.readFile(file, { encoding: 'utf-8' });
|
|
const splitLines = lines.split('\n');
|
|
return parseZoneFile(splitLines);
|
|
}
|