96 lines
2.4 KiB
TypeScript
96 lines
2.4 KiB
TypeScript
import * as fs from 'fs/promises';
|
|
import { DNSRecord, DNSZone, SOARecord } from '../models/interfaces';
|
|
import { DNSRecordType } from './records';
|
|
|
|
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 = '';
|
|
let 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);
|
|
}
|