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; actualLine = clean;
} }
const split = actualLine.split(' '); const split = actualLine.replace(/"\s"/g, '').split(' ');
if (split[0] === 'IN' && split[1] === 'NS') { if (split[0] === 'IN' && split[1] === 'NS') {
return { return {
name: '', name: '',

View File

@ -27,6 +27,7 @@ function createSOAString(record: SOARecord, padI: number, padJ: number): string[
export function createZoneFile(zone: DNSZone, preferredLineLength = 120): string[] { export function createZoneFile(zone: DNSZone, preferredLineLength = 120): string[] {
const file: string[] = []; const file: string[] = [];
file.push(`$TTL ${zone.ttl}`); file.push(`$TTL ${zone.ttl}`);
file.push(`; GENERATED BY icy-dyndns`);
let longestName = 0; let longestName = 0;
let longestType = 0; let longestType = 0;
@ -57,6 +58,8 @@ export function createZoneFile(zone: DNSZone, preferredLineLength = 120): string
file.push(`$INCLUDE ${include}`); file.push(`$INCLUDE ${include}`);
}); });
file.push('');
return file; 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 'express-async-errors';
import path from 'path'; import path from 'path';
import { DNSCache } from './dns/cache'; import { DNSCache } from './dns/cache';
import { DNSRecordType } from './dns/records'; import { DNSRecordType } from './dns/records';
import { createZoneFile } from './dns/writer'; import { createZoneFile } from './dns/writer';
import { fromRequest } from './ip/from-request'; 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 port = parseInt(process.env.PORT || '9129', 10);
const dir = process.env.ZONEFILES || '.'; const dir = process.env.ZONEFILES || '.';
@ -16,11 +16,14 @@ const api = express.Router();
app.use(express.json()); app.use(express.json());
const cache = new DNSCache(); 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> { async function getOrLoad(domain: string): Promise<CachedZone> {
if (!cache.has(domain)) { if (!cache.has(domain)) {
if (!weHave.includes(domain)) { if (!Object.values(authentic).includes(domain)) {
throw new Error('Invalid domain.'); throw new Error('Invalid domain.');
} }
return cache.load(domain, path.resolve(dir, `${domain}.zone`)); return cache.load(domain, path.resolve(dir, `${domain}.zone`));
@ -35,13 +38,43 @@ async function getOrLoad(domain: string): Promise<CachedZone> {
return get; return get;
} }
api.get('/records/:domain/download', async (req, res) => { api.use((req, res, next) => {
const domain = req.params.domain; const authHeader = req.get('authorization');
const cached = await getOrLoad(domain); if (!authHeader) {
res.send(createZoneFile(cached.zone).join('\n')); 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 domain = req.params.domain;
const cached = await getOrLoad(domain); const cached = await getOrLoad(domain);
@ -79,10 +112,10 @@ api.get('/records/:domain', async (req, res) => {
return; 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 domain = req.params.domain;
const index = parseInt(req.body.index, 10); const index = parseInt(req.body.index, 10);
const setters = req.body.record; 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 }); 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 domain = req.params.domain;
const index = parseInt(req.body.index, 10); 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 }); 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 domain = req.params.domain;
const setter = req.body.record; const setter = req.body.record;
@ -172,13 +205,45 @@ api.put('/records/:domain', async (req, res) => {
const cached = await getOrLoad(domain); const cached = await getOrLoad(domain);
const { zone } = cached; const { zone } = cached;
const newRecord = { name, type: upperType, value }; const newRecord = { name, type: upperType, value };
zone.records.push(newRecord); zone.records.push(newRecord);
await cache.update(domain, cached); await cache.update(domain, cached);
res.status(201).json({ success: true, message: 'Record added.', record: newRecord }); 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 domain = req.params.domain;
const subdomain = req.body.subdomain || '@'; const subdomain = req.body.subdomain || '@';
const waitPartial = req.body.dualRequest === true; const waitPartial = req.body.dualRequest === true;