tokens, ttl update
This commit is contained in:
parent
97d0a13165
commit
2dbb759b14
@ -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: '',
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
93
src/index.ts
93
src/index.ts
@ -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;
|
||||||
|
Reference in New Issue
Block a user