tokens, ttl update

This commit is contained in:
Evert Prants 2021-05-15 12:22:46 +03:00
parent 97d0a13165
commit 2dbb759b14
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
3 changed files with 83 additions and 15 deletions

View File

@ -25,7 +25,7 @@ function parseRecordLine(line: string, index: number, lines: string[]): DNSRecor
actualLine = clean;
}
const split = actualLine.split(' ');
const split = actualLine.replace(/"\s"/g, '').split(' ');
if (split[0] === 'IN' && split[1] === 'NS') {
return {
name: '',

View File

@ -27,6 +27,7 @@ function createSOAString(record: SOARecord, padI: number, padJ: number): string[
export function createZoneFile(zone: DNSZone, preferredLineLength = 120): string[] {
const file: string[] = [];
file.push(`$TTL ${zone.ttl}`);
file.push(`; GENERATED BY icy-dyndns`);
let longestName = 0;
let longestType = 0;
@ -57,6 +58,8 @@ export function createZoneFile(zone: DNSZone, preferredLineLength = 120): string
file.push(`$INCLUDE ${include}`);
});
file.push('');
return file;
}

View File

@ -1,11 +1,11 @@
import express, { ErrorRequestHandler, NextFunction, Request, Response } from 'express';
import express, { ErrorRequestHandler, NextFunction, Request, RequestHandler, Response } from 'express';
import 'express-async-errors';
import path from 'path';
import { DNSCache } from './dns/cache';
import { DNSRecordType } from './dns/records';
import { createZoneFile } from './dns/writer';
import { fromRequest } from './ip/from-request';
import { CachedZone, DNSRecord } from './models/interfaces';
import { CachedZone } from './models/interfaces';
const port = parseInt(process.env.PORT || '9129', 10);
const dir = process.env.ZONEFILES || '.';
@ -16,11 +16,14 @@ const api = express.Router();
app.use(express.json());
const cache = new DNSCache();
const weHave = ['lunasqu.ee'];
const authentic: {[x: string]: string} = {
'testing-token-auth-aaaa': 'lunasqu.ee',
'aaa': 'lol'
};
async function getOrLoad(domain: string): Promise<CachedZone> {
if (!cache.has(domain)) {
if (!weHave.includes(domain)) {
if (!Object.values(authentic).includes(domain)) {
throw new Error('Invalid domain.');
}
return cache.load(domain, path.resolve(dir, `${domain}.zone`));
@ -35,13 +38,43 @@ async function getOrLoad(domain: string): Promise<CachedZone> {
return get;
}
api.get('/records/:domain/download', async (req, res) => {
const domain = req.params.domain;
const cached = await getOrLoad(domain);
res.send(createZoneFile(cached.zone).join('\n'));
api.use((req, res, next) => {
const authHeader = req.get('authorization');
if (!authHeader) {
res.status(400).json({ success: false, message: 'Missing Authorization header' });
return;
}
const parts = authHeader.split(' ');
if (parts[0].toLowerCase() !== 'bearer') {
res.status(400).json({ success: false, message: 'Invalid Authorization header' });
return;
}
if (!parts[1] || !authentic[parts[1]]) {
res.status(401).json({ success: false, message: 'Unauthorized' });
return;
}
res.locals.token = parts[1];
next();
});
api.get('/records/:domain', async (req, res) => {
const domainAuthorization: RequestHandler = (req, res, next) => {
if (!req.params.domain || !res.locals.token) {
next(new Error('Unexpected bad request'));
return;
}
if (authentic[res.locals.token] !== req.params.domain) {
res.status(401).json({ success: false, message: 'Unauthorized access to domain' });
return;
}
next();
}
api.get('/zone/records/:domain', domainAuthorization, async (req, res) => {
const domain = req.params.domain;
const cached = await getOrLoad(domain);
@ -79,10 +112,10 @@ api.get('/records/:domain', async (req, res) => {
return;
}
res.json(cached.zone);
res.json(cached.zone.records);
});
api.post('/records/:domain', async (req, res) => {
api.post('/zone/records/:domain', domainAuthorization, async (req, res) => {
const domain = req.params.domain;
const index = parseInt(req.body.index, 10);
const setters = req.body.record;
@ -122,7 +155,7 @@ api.post('/records/:domain', async (req, res) => {
res.json({ success: true, message: 'Record changed successfully.', record });
});
api.delete('/records/:domain', async (req, res) => {
api.delete('/zone/records/:domain', domainAuthorization, async (req, res) => {
const domain = req.params.domain;
const index = parseInt(req.body.index, 10);
@ -143,7 +176,7 @@ api.delete('/records/:domain', async (req, res) => {
res.json({ success: true, message: 'Record deleted successfully.', record });
});
api.put('/records/:domain', async (req, res) => {
api.put('/zone/records/:domain', domainAuthorization, async (req, res) => {
const domain = req.params.domain;
const setter = req.body.record;
@ -172,13 +205,45 @@ api.put('/records/:domain', async (req, res) => {
const cached = await getOrLoad(domain);
const { zone } = cached;
const newRecord = { name, type: upperType, value };
zone.records.push(newRecord);
await cache.update(domain, cached);
res.status(201).json({ success: true, message: 'Record added.', record: newRecord });
});
api.post('/dyndns/:domain', async (req, res) => {
api.get('/zone/:domain/download', domainAuthorization, async (req, res) => {
const domain = req.params.domain;
const cached = await getOrLoad(domain);
res.send(createZoneFile(cached.zone).join('\n'));
});
api.get('/zone/:domain', domainAuthorization, async (req, res) => {
const domain = req.params.domain;
const cached = await getOrLoad(domain);
res.json(cached.zone);
});
api.post('/zone/:domain', domainAuthorization, async (req, res) => {
const domain = req.params.domain;
const cached = await getOrLoad(domain);
if (!req.body.ttl) {
res.json({ success: true, message: 'Nothing was changed.' });
return;
}
const numTTL = parseInt(req.body.TTL, 10);
if (!isNaN(numTTL)) {
cached.zone.ttl = numTTL;
}
await cache.update(domain, cached);
res.json({ success: true, message: 'TTL changed successfully.', ttl: cached.zone.ttl });
});
api.post('/dyndns/:domain', domainAuthorization, async (req, res) => {
const domain = req.params.domain;
const subdomain = req.body.subdomain || '@';
const waitPartial = req.body.dualRequest === true;