Compare commits
36 Commits
Author | SHA1 | Date |
---|---|---|
AJ ONeal | a4a3a89fd2 | |
AJ ONeal | 1517ba0a4b | |
AJ ONeal | d6f0dc83d3 | |
AJ ONeal | 9ca66313ec | |
AJ ONeal | b0a12260ea | |
AJ ONeal | 0e74e2db18 | |
AJ ONeal | 16bceb8a8a | |
AJ ONeal | 92ad26d18e | |
AJ ONeal | 49b1aa0ce7 | |
AJ ONeal | 98a64a3d34 | |
AJ ONeal | d5d1cf5782 | |
AJ ONeal | ae1720bed3 | |
AJ ONeal | 04057aa94a | |
AJ ONeal | f479c55501 | |
AJ ONeal | 9dfa8ed323 | |
AJ ONeal | 656c803720 | |
AJ ONeal | 92056f6d55 | |
AJ ONeal | 77b69d59a1 | |
AJ ONeal | 77e3096cd3 | |
AJ ONeal | d586e011e2 | |
AJ ONeal | 574b7472a2 | |
AJ ONeal | 8ecd7a8ebf | |
AJ ONeal | 2aa57739ed | |
AJ ONeal | 4612f2cbad | |
AJ ONeal | 7118cb6852 | |
AJ ONeal | 4f5f330862 | |
AJ ONeal | 7a36e5f685 | |
AJ ONeal | fa45842f9f | |
AJ ONeal | 8d329b93b2 | |
AJ ONeal | 72e920c1dd | |
AJ ONeal | b2ced1a492 | |
AJ ONeal | 96dd311fc5 | |
AJ ONeal | 66a267090b | |
AJ ONeal | 94cf2aa4bc | |
AJ ONeal | abaed056e5 | |
AJ ONeal | e2393d965a |
17
bin/digd.js
17
bin/digd.js
|
@ -34,6 +34,7 @@ cli.parse({
|
|||
//, 'serve': [ 's', 'path to json file with array of responses to issue for given queries', 'string' ]
|
||||
//, 'type': [ 't', 'type (defaults to ANY for dns and PTR for mdns)', 'string' ]
|
||||
//, 'query': [ 'q', 'a superfluous explicit option to set the query as a command line flag' ]
|
||||
, 'http': [ false, 'enable http on the specified port', 'int' ]
|
||||
});
|
||||
|
||||
cli.main(function (args, cli) {
|
||||
|
@ -95,6 +96,9 @@ cli.main(function (args, cli) {
|
|||
}
|
||||
}
|
||||
|
||||
var engine;
|
||||
var path = require('path');
|
||||
var engineOpts = { filepath: path.resolve(cli.input) };
|
||||
var dnsd = {};
|
||||
dnsd.onMessage = function (nb, cb) {
|
||||
var byteOffset = nb._dnsByteOffset || nb.byteOffset;
|
||||
|
@ -364,7 +368,6 @@ cli.main(function (args, cli) {
|
|||
}
|
||||
|
||||
// TODO get local answer first, if available
|
||||
var path = require('path');
|
||||
if (!cli.input) {
|
||||
console.warn('[WARN] no db path given, must recurse if enabled');
|
||||
recurse();
|
||||
|
@ -383,9 +386,8 @@ cli.main(function (args, cli) {
|
|||
sendResponse(resp);
|
||||
}
|
||||
|
||||
var engine;
|
||||
try {
|
||||
engine = require('../lib/store.json.js').create({ filepath: path.resolve(cli.input) });
|
||||
engine = engine || require('../lib/store.json.js').create(engineOpts);
|
||||
} catch(e) {
|
||||
respondWithResults(e);
|
||||
return;
|
||||
|
@ -409,6 +411,15 @@ cli.main(function (args, cli) {
|
|||
if (cli.tcp /* TODO v1.3 !cli.notcp */) {
|
||||
require('../lib/tcpd.js').create(cli, dnsd);
|
||||
}
|
||||
if (cli.http) {
|
||||
try {
|
||||
engine = engine || require('../lib/store.json.js').create(engineOpts);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
return;
|
||||
}
|
||||
require('../lib/httpd.js').create(cli, engine, dnsd);
|
||||
}
|
||||
|
||||
console.log('');
|
||||
if (!cli.nocmd) {
|
||||
|
|
79
lib/digd.js
79
lib/digd.js
|
@ -16,7 +16,7 @@ function getRecords(engine, qname, cb) {
|
|||
// SECURITY XXX TODO var dig = require('dig.js/dns-request');
|
||||
var count;
|
||||
|
||||
return engine.getRecords({ name: qname }, function (err, myRecords) {
|
||||
return engine.records.get({ name: qname }, function (err, myRecords) {
|
||||
if (err) { cb(err); return; }
|
||||
|
||||
function checkCount() {
|
||||
|
@ -67,7 +67,6 @@ function getRecords(engine, qname, cb) {
|
|||
// TODO allow multiple records to be returned(?)
|
||||
return function (err, addresses) {
|
||||
if (err || !addresses.length) {
|
||||
r.id = r.id || Math.random();
|
||||
delMe[r.id] = true;
|
||||
} else if (addresses.length > 1) {
|
||||
r._address = addresses[Math.floor(Math.random() * addresses.length)];
|
||||
|
@ -139,19 +138,19 @@ function dbToResourceRecord(r) {
|
|||
};
|
||||
}
|
||||
|
||||
function getNs(engine, ds, results, cb) {
|
||||
console.log('[DEV] getNs entered with domains', ds);
|
||||
function getNs(engine, zs, results, cb) {
|
||||
console.log('[DEV] getNs entered with domains', zs);
|
||||
|
||||
var d = ds.shift();
|
||||
console.log('[DEV] trying another one', d);
|
||||
var z = zs.shift();
|
||||
console.log('[DEV] trying another one', z);
|
||||
|
||||
if (!d) {
|
||||
if (!z) {
|
||||
results.header.rcode = NXDOMAIN;
|
||||
cb(null, results);
|
||||
return;
|
||||
}
|
||||
|
||||
var qn = d.id.toLowerCase();
|
||||
var qn = z.name.toLowerCase();
|
||||
|
||||
return getRecords(engine, qn, function (err, records) {
|
||||
if (err) { cb(err); return; }
|
||||
|
@ -179,16 +178,16 @@ function getNs(engine, ds, results, cb) {
|
|||
});
|
||||
|
||||
if (!results.authority.length) {
|
||||
return getNs(engine, ds, results, cb);
|
||||
return getNs(engine, zs, results, cb);
|
||||
}
|
||||
|
||||
// d.vanityNs should only be vanity nameservers (pointing to this same server)
|
||||
if (d.vanityNs || results.authority.some(function (ns) {
|
||||
if (z.vanityNs || results.authority.some(function (ns) {
|
||||
console.log('[debug] ns', ns);
|
||||
return -1 !== engine.primaryNameservers.indexOf(ns.data.toLowerCase());
|
||||
})) {
|
||||
results.authority.length = 0;
|
||||
results.authority.push(domainToSoa(engine.primaryNameservers, d));
|
||||
results.authority.push(engine.zones._toSoa(z));
|
||||
results.header.rcode = NXDOMAIN;
|
||||
}
|
||||
cb(null, results);
|
||||
|
@ -196,54 +195,13 @@ function getNs(engine, ds, results, cb) {
|
|||
});
|
||||
}
|
||||
|
||||
function domainToSoa(primaryNameservers, domain) {
|
||||
var nameservers = domain.vanityNs || primaryNameservers;
|
||||
|
||||
var index = Math.floor(Math.random() * nameservers.length) % nameservers.length;
|
||||
var nameserver = nameservers[index];
|
||||
return {
|
||||
name: domain.id
|
||||
, typeName: 'SOA'
|
||||
, className: 'IN'
|
||||
, ttl: domain.ttl || 60
|
||||
|
||||
// nameserver -- select an NS at random if they're all in sync
|
||||
, primary: nameserver
|
||||
, name_server: nameserver
|
||||
|
||||
// admin -- email address or domain for admin
|
||||
, admin: domain.admin || ('admin.' + domain.id)
|
||||
, email_addr: domain.admin || ('admin.' + domain.id)
|
||||
|
||||
// serial -- the version, for cache-busting of secondary nameservers. suggested format: YYYYMMDDnn
|
||||
, serial: domain.serial || Math.round((domain.updatedAt || domain.createdAt || 0) / 1000)
|
||||
, sn: domain.serial || Math.round((domain.updatedAt || domain.createdAt || 0) / 1000)
|
||||
|
||||
// refresh -- only used when nameservers following the DNS NOTIFY spec talk
|
||||
, refresh: domain.refresh || 1800
|
||||
, ref: domain.refresh || 1800
|
||||
|
||||
// retry -- only used when nameservers following the DNS NOTIFY spec talk
|
||||
, retry: domain.retry || 600
|
||||
, ret: domain.retry || 600
|
||||
|
||||
// expiration -- how long other nameservers should continue when the primary goes down
|
||||
, expiration: domain.expiration || 2419200
|
||||
, ex: domain.expiration || 2419200
|
||||
|
||||
// minimum -- how long to cache a non-existent domain (also the default ttl for BIND)
|
||||
, minimum: domain.minimum || 5
|
||||
, nx: domain.minimum || 5
|
||||
};
|
||||
}
|
||||
|
||||
function getSoa(primaryNameservers, domain, results, cb, answerSoa) {
|
||||
function getSoa(engine, domain, results, cb, answerSoa) {
|
||||
console.log('[DEV] getSoa entered');
|
||||
|
||||
if (!answerSoa) {
|
||||
results.authority.push(domainToSoa(primaryNameservers, domain));
|
||||
results.authority.push(engine.zones._toSoa(domain));
|
||||
} else {
|
||||
results.answer.push(domainToSoa(primaryNameservers, domain));
|
||||
results.answer.push(engine.zones._toSoa(domain));
|
||||
}
|
||||
|
||||
cb(null, results);
|
||||
|
@ -317,7 +275,7 @@ module.exports.query = function (engine, query, cb) {
|
|||
var qarr = qname.split('.');
|
||||
var qnames = [];
|
||||
while (qarr.length) {
|
||||
qnames.push(qarr.join('.').toLowerCase());
|
||||
qnames.push({ name: qarr.join('.').toLowerCase() });
|
||||
qarr.shift(); // first
|
||||
}
|
||||
|
||||
|
@ -326,7 +284,8 @@ module.exports.query = function (engine, query, cb) {
|
|||
console.log('[DEV] qnames');
|
||||
console.log(qnames);
|
||||
|
||||
return engine.getSoas({ names: qnames}, function (err, myDomains) {
|
||||
// getSoas
|
||||
return engine.zones.get(qnames, function (err, myDomains) {
|
||||
console.log('[SOA] looking for', qnames, 'and proudly serving', err, myDomains);
|
||||
if (err) { cb(err); return; }
|
||||
|
||||
|
@ -350,10 +309,10 @@ module.exports.query = function (engine, query, cb) {
|
|||
//console.log('sorted domains', myDomains);
|
||||
|
||||
if (!getNsAlso) {
|
||||
return getSoa(engine.primaryNameservers, myDomains[0], results, cb, answerSoa);
|
||||
return getSoa(engine, myDomains[0], results, cb, answerSoa);
|
||||
}
|
||||
|
||||
return getNs(engine, /*myDomains.slice(0)*/qnames.map(function (qn) { return { id: qn }; }), results, function (err, results) {
|
||||
return getNs(engine, /*myDomains.slice(0)*/qnames, results, function (err, results) {
|
||||
//console.log('[DEV] getNs complete');
|
||||
|
||||
if (err) { cb(err, results); return; }
|
||||
|
@ -364,7 +323,7 @@ module.exports.query = function (engine, query, cb) {
|
|||
}
|
||||
|
||||
// myDomains was sorted such that the longest was first
|
||||
return getSoa(engine.primaryNameservers, myDomains[0], results, cb);
|
||||
return getSoa(engine, myDomains[0], results, cb);
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,325 @@
|
|||
'use strict';
|
||||
|
||||
module.exports.create = function (cli, engine/*, dnsd*/) {
|
||||
var subparts = (cli.subject || '').split('@');
|
||||
/*
|
||||
{
|
||||
"kty": "EC",
|
||||
"use": "sig",
|
||||
"crv": "P-256",
|
||||
"x": "ogbK2nP6SiEIIp4w8oXBn3dcs6kljFfTbgZYG591tUU",
|
||||
"y": "sB0AekMYwpvbQfAoW-2LlEWdapNhxynfj1zBtWpE9lo",
|
||||
"alg": "ES256"
|
||||
}
|
||||
*/
|
||||
var jwt;
|
||||
var jwk;
|
||||
var privpem;
|
||||
var pubpem;
|
||||
if (!subparts[1]) {
|
||||
subparts = [ 'root', 'localhost' ];
|
||||
// TODO generate new random key and store it
|
||||
jwk = cli.privkey || require('../samples/privkey.js');
|
||||
jwt = require('jsonwebtoken');
|
||||
privpem = require('jwk-to-pem')(jwk, { private: true });
|
||||
pubpem = require('jwk-to-pem')(jwk, { private: false });
|
||||
console.info("====================");
|
||||
console.info(" EC256 Private PEM: ");
|
||||
console.info("====================");
|
||||
console.info(privpem);
|
||||
console.info();
|
||||
console.info("================================");
|
||||
console.info(" JWT Write Authorization Token: ");
|
||||
console.info("================================");
|
||||
console.info(jwt.sign(
|
||||
{ sub: subparts[0]
|
||||
, iss: subparts[1]
|
||||
, aud: 'localhost'
|
||||
, scp: '+rw@adns.org'
|
||||
}
|
||||
, privpem
|
||||
, { notBefore: 0 // from now
|
||||
, expiresIn: '2h'
|
||||
, algorithm: 'ES256'
|
||||
}
|
||||
));
|
||||
// expressed as "from now"
|
||||
/*
|
||||
{ NotBeforeError: jwt not active
|
||||
at Object.module.exports [as verify] (digd.js/node_modules/jsonwebtoken/verify.js:117:19)
|
||||
at digd.js/lib/httpd.js:112:15
|
||||
at Layer.handle [as handle_request] (digd.js/node_modules/express/lib/router/layer.js:95:5)
|
||||
at trim_prefix (digd.js/node_modules/express/lib/router/index.js:317:13)
|
||||
at digd.js/node_modules/express/lib/router/index.js:284:7
|
||||
at Function.process_params (digd.js/node_modules/express/lib/router/index.js:335:12)
|
||||
at next (digd.js/node_modules/express/lib/router/index.js:275:10)
|
||||
at expressInit (digd.js/node_modules/express/lib/middleware/init.js:40:5)
|
||||
at Layer.handle [as handle_request] (digd.js/node_modules/express/lib/router/layer.js:95:5)
|
||||
at trim_prefix (digd.js/node_modules/express/lib/router/index.js:317:13)
|
||||
name: 'NotBeforeError',
|
||||
message: 'jwt not active',
|
||||
date: +050046-12-28T01:12:58.000Z }
|
||||
*/
|
||||
console.info("===============================");
|
||||
console.info(" JWT Read Authorization Token: ");
|
||||
console.info("===============================");
|
||||
console.info(jwt.sign(
|
||||
{ sub: subparts[0]
|
||||
, iss: subparts[1]
|
||||
, aud: 'localhost'
|
||||
, scp: '+r@adns.org'
|
||||
}
|
||||
, privpem
|
||||
, { notBefore: 0 // from now
|
||||
, algorithm: 'ES256'
|
||||
}
|
||||
));
|
||||
console.info("==========================");
|
||||
}
|
||||
|
||||
function runHttp() {
|
||||
var path = require('path');
|
||||
var express = require('express');
|
||||
var jsonParser = require('body-parser').json({ strict: true, limit: '100kb' });
|
||||
var app = express();
|
||||
var httpServer = require('http').createServer(app);
|
||||
|
||||
function hasClaim(claim) {
|
||||
return function (req, res, next) {
|
||||
if ((req.token.scp||'').split(/[\s,]/g).some(function (c) {
|
||||
return claim === c;
|
||||
})) {
|
||||
next();
|
||||
} else {
|
||||
res.send({ error: { message: "no claim to '" + claim + "' in token" } });
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
app.use('/api', function (req, res, next) {
|
||||
var auth = ((req.headers.authorization || '').split(/\s+/)[1] || req.query.access_token);
|
||||
var token;
|
||||
|
||||
if (!auth) {
|
||||
res.statusCode = 403;
|
||||
res.send({ error: { message: "need authorization" } });
|
||||
return;
|
||||
}
|
||||
|
||||
jwt = jwt || require('jsonwebtoken');
|
||||
try {
|
||||
token = jwt.decode(auth);
|
||||
} catch (e) {
|
||||
token = null;
|
||||
}
|
||||
|
||||
if (!token || !token.iss) {
|
||||
res.statusCode = 403;
|
||||
res.send({ error: { message: "need jwt-format authorization" } });
|
||||
return;
|
||||
}
|
||||
|
||||
if (subparts[0] === token.sub && subparts[1] === token.iss) {
|
||||
try {
|
||||
/*
|
||||
// { algorithm: 'ES256' }
|
||||
{ JsonWebTokenError: invalid algorithm
|
||||
at Object.module.exports [as verify] (digd.js/node_modules/jsonwebtoken/verify.js:90:17)
|
||||
at digd.js/lib/httpd.js:82:15
|
||||
at Layer.handle [as handle_request] (digd.js/node_modules/express/lib/router/layer.js:95:5)
|
||||
at trim_prefix (digd.js/node_modules/express/lib/router/index.js:317:13)
|
||||
at digd.js/node_modules/express/lib/router/index.js:284:7
|
||||
at Function.process_params (digd.js/node_modules/express/lib/router/index.js:335:12)
|
||||
at next (digd.js/node_modules/express/lib/router/index.js:275:10)
|
||||
at expressInit (digd.js/node_modules/express/lib/middleware/init.js:40:5)
|
||||
at Layer.handle [as handle_request] (digd.js/node_modules/express/lib/router/layer.js:95:5)
|
||||
at trim_prefix (digd.js/node_modules/express/lib/router/index.js:317:13) name: 'JsonWebTokenError', message: 'invalid algorithm' }
|
||||
*/
|
||||
// could be that it's private but expecting public, or public but expecting private
|
||||
/*
|
||||
Error: error:0906D06C:PEM routines:PEM_read_bio:no start line
|
||||
at Verify.verify (crypto.js:381:23)
|
||||
at verify (digd.js/node_modules/jwa/index.js:68:21)
|
||||
at Object.verify (digd.js/node_modules/jwa/index.js:85:18)
|
||||
at Object.jwsVerify [as verify] (digd.js/node_modules/jws/lib/verify-stream.js:54:15)
|
||||
at Object.module.exports [as verify] (digd.js/node_modules/jsonwebtoken/verify.js:96:17)
|
||||
at digd.js/lib/httpd.js:82:15
|
||||
at Layer.handle [as handle_request] (digd.js/node_modules/express/lib/router/layer.js:95:5)
|
||||
at trim_prefix (digd.js/node_modules/express/lib/router/index.js:317:13)
|
||||
at digd.js/node_modules/express/lib/router/index.js:284:7
|
||||
at Function.process_params (digd.js/node_modules/express/lib/router/index.js:335:12)
|
||||
*/
|
||||
jwt.verify(auth, pubpem, { algorithms: [ 'ES256' ] });
|
||||
} catch(e) {
|
||||
res.statusCode = 403;
|
||||
console.error(e);
|
||||
console.log(auth);
|
||||
console.log(jwt.decode(auth, { complete: true }));
|
||||
res.send({ error: { message: "jwt was not verified authorization" } });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
req.auth = auth;
|
||||
req.token = token;
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/api/verify-auth', function (req, res) {
|
||||
res.send({ success: true });
|
||||
});
|
||||
app.get('/api/peers', function (req, res) {
|
||||
engine.peers.all(function (err, peers) {
|
||||
res.send({ peers: peers });
|
||||
});
|
||||
});
|
||||
app.get('/api/zones', function (req, res) {
|
||||
engine.zones.all(function (err, zones) {
|
||||
res.send({ zones: zones });
|
||||
});
|
||||
});
|
||||
app.post('/api/zones', jsonParser, hasClaim('+rw@adns.org'), function (req, res) {
|
||||
var upzone = req.body || {};
|
||||
console.log('create zone', upzone);
|
||||
engine.zones.save(upzone, function (err, zone) {
|
||||
if (err) { res.send({ error: { message: err.message } }); return; }
|
||||
console.log('create zone result', zone);
|
||||
res.send(zone);
|
||||
});
|
||||
});
|
||||
app.delete('/api/zones/:id', hasClaim('+rw@adns.org'), function (req, res) {
|
||||
var zoneId = req.params.id;
|
||||
engine.zones.destroy(zoneId, function (err, zone) {
|
||||
if (err) { res.send({ error: { message: err.message } }); return; }
|
||||
// zone + records
|
||||
res.send(zone);
|
||||
});
|
||||
});
|
||||
function mapRecord(r) {
|
||||
return {
|
||||
id: r.id
|
||||
, soa: r.soa
|
||||
, zone: r.zone
|
||||
, name: r.name
|
||||
, tld: r.tld
|
||||
, type: r.type
|
||||
, class: r.class
|
||||
, ttl: r.ttl
|
||||
, data: r.data
|
||||
, address: r.address
|
||||
, exchange: r.exchange
|
||||
, priority: r.priority
|
||||
, value: r.value
|
||||
, aname: r.aname
|
||||
, flag: r.flag
|
||||
, tag: r.tag
|
||||
, weight: r.weight
|
||||
, port: r.port
|
||||
, target: r.target
|
||||
};
|
||||
}
|
||||
app.get('/api/zones/:zone/records', function (req, res) {
|
||||
var zonename = req.params.zone;
|
||||
// [{ name: zonename }]
|
||||
engine.zones.get({ names: [ zonename ] }, function (err, zones) {
|
||||
console.log('zone:');
|
||||
console.log(zones[0]);
|
||||
var zone = engine.zones._toSoa(zones[0]);
|
||||
zone.class = zone.className;
|
||||
zone.type = zone.typeName;
|
||||
zone.soa = true;
|
||||
engine.records.all(function (err, records) {
|
||||
records = records.filter(function (r) {
|
||||
return r.zone === zonename;
|
||||
}).map(mapRecord);
|
||||
records.unshift(zone);
|
||||
res.send({ records: records });
|
||||
});
|
||||
});
|
||||
});
|
||||
app.get('/api/records', function (req, res) {
|
||||
engine.records.all(function (err, records) {
|
||||
res.send({ records: records.map(mapRecord) });
|
||||
});
|
||||
});
|
||||
app.get('/api/records/:name', function (req, res) {
|
||||
engine.records.all(function (err, records) {
|
||||
res.send({ records: records.filter(function (r) {
|
||||
if (r.name === req.params.name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var parts = req.params.name.split('.');
|
||||
parts.shift();
|
||||
if ('*.' + parts.join('.') === r.name) {
|
||||
return true;
|
||||
}
|
||||
}).map(mapRecord) });
|
||||
});
|
||||
});
|
||||
app.post('/api/records/:id?', jsonParser, hasClaim('+rw@adns.org'), function (req, res) {
|
||||
var record = req.body || {};
|
||||
record.id = req.params.id || record.id;
|
||||
|
||||
if ('SOA' === record.type) {
|
||||
// TODO be strict about what can be edited
|
||||
engine.zones.save(record, function (err, zone) {
|
||||
if (err) {
|
||||
res.send({ error: { message: err.message } });
|
||||
return;
|
||||
}
|
||||
// { success: true }
|
||||
res.send(zone);
|
||||
});
|
||||
} else {
|
||||
engine.records.save(record, function (err, record) {
|
||||
res.send({ success: true, id: record.id });
|
||||
});
|
||||
}
|
||||
});
|
||||
app.delete('/api/records/:id', jsonParser, hasClaim('+rw@adns.org'), function (req, res) {
|
||||
var id = req.params.id;
|
||||
|
||||
engine.records.one(id, function (err, record) {
|
||||
if (err) {
|
||||
res.send({ error: { message: err.message } });
|
||||
return;
|
||||
}
|
||||
if (!record) {
|
||||
res.send({ error: { message: "no record with id '" + id + "' found" } });
|
||||
return;
|
||||
}
|
||||
|
||||
if ('SOA' === record.type) {
|
||||
// TODO be strict about what can be edited
|
||||
engine.zones.destroy(id, function (err, record) {
|
||||
if (!err) {
|
||||
res.send(record);
|
||||
return;
|
||||
}
|
||||
res.send({ error: { message: err.message } });
|
||||
});
|
||||
} else {
|
||||
engine.records.destroy(id, function (err, record) {
|
||||
if (!err) {
|
||||
res.send(record || { error: { message: "no record with id '" + id + "' found" } });
|
||||
return;
|
||||
}
|
||||
res.send({ error: { message: err.message } });
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.use('/', express.static(path.join(__dirname, 'public')));
|
||||
|
||||
httpServer.listen(cli.http, function () {
|
||||
console.info(httpServer.address().address + '#' + httpServer.address().port + ' (http)');
|
||||
});
|
||||
}
|
||||
|
||||
runHttp();
|
||||
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
input[class*="js-record-"] {
|
||||
text-align: right
|
||||
}
|
|
@ -0,0 +1,274 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>ADNS</title>
|
||||
<link href="css/style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>ADNS Zones and Records</h1>
|
||||
<p>
|
||||
<label>API JWT:</label> <input class="js-jwt" type="text" placeholder="paste the api token here" />
|
||||
<button class="js-jwt" type="button">Authorize</button>
|
||||
</p>
|
||||
|
||||
<p><a class="js-peers" data-href="/api/peers?access_token=:token" target="adns-json">/api/peers</a></p>
|
||||
<p><a class="js-zones" data-href="/api/zones?access_token=:token" target="adns-json">/api/zones</a></p>
|
||||
<p><a data-href="/api/zones/:zone/records?access_token=:token" class="js-zone" target="adns-json">/api/zones/<code
|
||||
class="js-zone">:zone</code>/records</a>
|
||||
<input class="js-zone"
|
||||
type="text" placeholder="example.com" /></p>
|
||||
<p><a class="js-records" data-href="/api/records?access_token=:token" target="adns-json">/api/records</a></p>
|
||||
<p><a data-href="/api/records/:name?access_token=:token" class="js-name" target="adns-json">/api/records/<code class="js-name">:name</code></a>
|
||||
<input class="js-name"
|
||||
type="text" placeholder="example.com"/></p>
|
||||
|
||||
<h3>Peers:</h3>
|
||||
<ul class="js-peer-tpl">
|
||||
<li class="js-peer">
|
||||
<span class="js-peer-name">nsx.example.com</span>
|
||||
<span class="js-peer-address">127.0.0.1</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Zones:</h3>
|
||||
<form class="js-zone-form-create">
|
||||
<input type="text" class="js-zone-form-name" placeholder="ex: example.com" />
|
||||
<button type="button" class="js-zone-new">Create Zone</button>
|
||||
</form>
|
||||
<ul class="js-zone-tpl">
|
||||
<li class="js-zone">
|
||||
<span class="js-zone-name">example.com</span>
|
||||
<button type="button" class="js-zone-view">view</button>
|
||||
<button type="button" class="js-zone-edit">edit</button>
|
||||
<button type="button" class="js-zone-destroy">X</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="js-zone-form-tpl">
|
||||
<form class="js-zone-form-soa">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<span class="js-record-type">SOA</span>
|
||||
<input type="text" class="js-record-name" placeholder="[zone name] example.com" disabled>
|
||||
<input type="text" class="js-recordx-primary" placeholder="[primary] ns{{x}}.example.com" disabled>
|
||||
<input type="text" class="js-record-admin" placeholder="[admin] admin.example.com">
|
||||
<input type="text" class="js-record-expiration" placeholder="[expire] 2419200 (how long to rely on non-primary)">
|
||||
<input type="text" class="js-record-minimum" placeholder="[minimum nxdomain] 5">
|
||||
<input type="text" class="js-recordx-serial" placeholder="[serial] YYYYMMDDxx" disabled>
|
||||
<input type="text" class="js-record-retry" placeholder="[retry] 1800 (how quickly to retry on failed update)">
|
||||
<input type="text" class="js-record-refresh" placeholder="[refresh] 7200 (how often to check for updates)">
|
||||
<input type="text" class="js-record-ttl" placeholder="[default record ttl] 86400">
|
||||
</form>
|
||||
<span class="js-record-type">Vanity Nameservers:</span>
|
||||
<label><input type="checkbox" class="js-zone-form-vanityns" value=""> use vanity nameservers (requires glue records to be set at your domain reseller/registrar)</label>
|
||||
<div class="js-zone-form-vns-tpl">
|
||||
<form class="js-zone-form-vns">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<input type="text" class="js-record-host" placeholder="nsx">.<span class="js-record-zone">example.com</span>
|
||||
<input type="text" class="js-record-address" placeholder="aname, ipv4, or ipv6 address">
|
||||
<input type="text" class="js-record-ttl" placeholder="86400">
|
||||
<!--
|
||||
<button type="button" class="js-record-remove">-</button>
|
||||
<button type="button" class="js-record-add">+</button>
|
||||
-->
|
||||
</form>
|
||||
</div>
|
||||
<button type="button" class="js-zone-save">Save Zone</button>
|
||||
</div>
|
||||
|
||||
<h3>Records:</h3>
|
||||
<ul class="js-record-tpl">
|
||||
<li class="js-record-soa">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<span class="js-record-type">SOA</span>
|
||||
<span class="js-record-name">example.com</span>
|
||||
<span class="js-record-primary">{{ primary }}</span>
|
||||
<span class="js-record-admin">admin.example.com</span>
|
||||
<span class="js-record-expiration">{{ seconds until expiration }}</span>
|
||||
<span class="js-record-minimum">{{ minimum }}</span>
|
||||
<span class="js-record-serial">{{ serial }}</span>
|
||||
<span class="js-record-retry">{{ retry }}</span>
|
||||
<span class="js-record-refresh">{{ refresh }}</span>
|
||||
<span class="js-record-ttl">{{ ttl }}</span>
|
||||
<button type="button" class="js-record-edit">Edit</button>
|
||||
<button type="button" class="js-record-destroy">X</button>
|
||||
</li>
|
||||
<li class="js-record-a js-record-aaaa">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<span class="js-record-type">A / AAAA</span>
|
||||
<span class="js-record-name">example.com</span>
|
||||
<span class="js-record-address">{{ addr }}</span>
|
||||
<span class="js-record-ttl">{{ ttl }}</span>
|
||||
<button type="button" class="js-record-edit">Edit</button>
|
||||
<button type="button" class="js-record-destroy">X</button>
|
||||
</li>
|
||||
<li class="js-record-aname js-record-cname js-record-ns">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<span class="js-record-type">ANAME / CNAME</span>
|
||||
<span class="js-record-name">example.com</span>
|
||||
<span class="js-record-data">{{ target }}</span>
|
||||
<span class="js-record-ttl">{{ ttl }}</span>
|
||||
<button type="button" class="js-record-edit">Edit</button>
|
||||
<button type="button" class="js-record-destroy">X</button>
|
||||
</li>
|
||||
<li class="js-record-caa">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<span class="js-record-type">CAA</span>
|
||||
<span class="js-record-name">example.com</span>
|
||||
<span class="js-record-flag">{{ flag }}</span>
|
||||
<span class="js-record-value">{{ value }}</span>
|
||||
<span class="js-record-ttl">{{ ttl }}</span>
|
||||
<button type="button" class="js-record-edit">Edit</button>
|
||||
<button type="button" class="js-record-destroy">X</button>
|
||||
</li>
|
||||
<li class="js-record-mx">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<span class="js-record-type">MX</span>
|
||||
<span class="js-record-name">example.com</span>
|
||||
<span class="js-record-exchange">{{ target }}</span>
|
||||
<span class="js-record-priority">{{ priority }}</span>
|
||||
<span class="js-record-ttl">{{ ttl }}</span>
|
||||
<button type="button" class="js-record-edit">Edit</button>
|
||||
<button type="button" class="js-record-destroy">X</button>
|
||||
</li>
|
||||
<li class="js-record-ptr">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<span class="js-record-type">PTR</span>
|
||||
<span class="js-record-name">example.com</span>
|
||||
<span class="js-record-ttl">{{ ttl }}</span>
|
||||
<button type="button" class="js-record-edit">Edit</button>
|
||||
<button type="button" class="js-record-destroy">X</button>
|
||||
</li>
|
||||
<li class="js-record-srv">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<span class="js-record-type">SRV</span>
|
||||
<span class="js-record-name">example.com</span>
|
||||
<span class="js-record-port">{{ port }}</span>
|
||||
<span class="js-record-priority">{{ priority }}</span>
|
||||
<span class="js-record-ttl">{{ ttl }}</span>
|
||||
<button type="button" class="js-record-edit">Edit</button>
|
||||
<button type="button" class="js-record-destroy">X</button>
|
||||
</li>
|
||||
<li class="js-record-txt">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<span class="js-record-type">TXT</span>
|
||||
<span class="js-record-name">example.com</span>
|
||||
<span class="js-record-data">{{ text data }}</span>
|
||||
<span class="js-record-ttl">{{ ttl }}</span>
|
||||
<button type="button" class="js-record-edit">Edit</button>
|
||||
<button type="button" class="js-record-destroy">X</button>
|
||||
</li>
|
||||
<li class="js-record-typex">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<span class="js-record-type">type000</span>
|
||||
<span class="js-record-name">example.com</span>
|
||||
<span class="js-record-rr">{{ hex }}</span>
|
||||
<span class="js-record-ttl">{{ ttl }}</span>
|
||||
<button type="button" class="js-record-edit">Edit</button>
|
||||
<button type="button" class="js-record-destroy">X</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<label>Select Type:</label>
|
||||
<select class="js-record-form-type">
|
||||
<option value="" selected disabled>Record Type</option>
|
||||
<option value="SOA" disabled>SOA</option>
|
||||
<option value="NS">NS (delegation)</option>
|
||||
<option value="A">A</option>
|
||||
<option value="AAAA">AAAA</option>
|
||||
<option value="ANAME">ANAME</option>
|
||||
<option value="CNAME">CNAME</option>
|
||||
<option value="CAA">CAA</option>
|
||||
<option value="MX">MX</option>
|
||||
<option value="PTR">PTR</option>
|
||||
<option value="SRV">SRV</option>
|
||||
<option value="TXT">TXT</option>
|
||||
<option value="typeX">typeX</option>
|
||||
</select>
|
||||
<button type="button" class="js-record-form-new">Create Record</button>
|
||||
|
||||
<div class="js-record-form-tpl">
|
||||
<form class="js-record-form-soa">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<span class="js-record-type">SOA</span>
|
||||
<input type="text" class="js-record-name">
|
||||
<input type="text" class="js-recordx-primary" placeholder="ns{{rnd}}.example.com" disabled>
|
||||
<input type="text" class="js-record-admin">
|
||||
<input type="text" class="js-record-expiration">
|
||||
<input type="text" class="js-record-minimum">
|
||||
<input type="text" class="js-record-serial" disabled>
|
||||
<input type="text" class="js-record-retry">
|
||||
<input type="text" class="js-record-refresh">
|
||||
<input type="text" class="js-record-ttl">
|
||||
<button type="button" class="js-record-save">save</button>
|
||||
</form>
|
||||
<form class="js-record-form-a js-record-form-aaaa">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<span class="js-record-type">A / AAAA</span>
|
||||
<input type="text" class="js-record-host">.<span class="js-record-zone">example.com</span>
|
||||
<input type="text" class="js-record-address" placeholder="ex: 127.0.0.1">
|
||||
<input type="text" class="js-record-ttl" placeholder="ex: 300">
|
||||
<button type="button" class="js-record-save">save</button>
|
||||
</form>
|
||||
<form class="js-record-form-aname js-record-form-cname js-record-form-ns">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<span class="js-record-type">ANAME / CNAME</span>
|
||||
<input type="text" class="js-record-host">.<span class="js-record-zone">example.com</span>
|
||||
<input type="text" class="js-record-data">
|
||||
<input type="text" class="js-record-ttl">
|
||||
<button type="button" class="js-record-save">save</button>
|
||||
</form>
|
||||
<form class="js-record-form-caa">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<span class="js-record-type">CAA</span>
|
||||
<input type="text" class="js-record-host">.<span class="js-record-zone">example.com</span>
|
||||
<input type="text" class="js-record-flag">
|
||||
<input type="text" class="js-record-value">
|
||||
<input type="text" class="js-record-ttl">
|
||||
<button type="button" class="js-record-save">save</button>
|
||||
</form>
|
||||
<form class="js-record-form-mx">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<span class="js-record-type">MX</span>
|
||||
<input type="text" class="js-record-host">.<span class="js-record-zone">example.com</span>
|
||||
<input type="text" class="js-record-exchange">
|
||||
<input type="text" class="js-record-priority">
|
||||
<input type="text" class="js-record-ttl">
|
||||
<button type="button" class="js-record-save">save</button>
|
||||
</form>
|
||||
<form class="js-record-form-ptr">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<span class="js-record-type">PTR</span>
|
||||
<input type="text" class="js-record-host">.<span class="js-record-zone">example.com</span>
|
||||
<input type="text" class="js-record-ttl">
|
||||
<button type="button" class="js-record-save">save</button>
|
||||
</form>
|
||||
<form class="js-record-form-srv">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<span class="js-record-type">SRV</span>
|
||||
<input type="text" class="js-record-host">.<span class="js-record-zone">example.com</span>
|
||||
<input type="text" class="js-record-port">
|
||||
<input type="text" class="js-record-priority">
|
||||
<input type="text" class="js-record-ttl">
|
||||
<button type="button" class="js-record-save">save</button>
|
||||
</form>
|
||||
<form class="js-record-form-txt">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<span class="js-record-type">TXT</span>
|
||||
<input type="text" class="js-record-host">.<span class="js-record-zone">example.com</span>
|
||||
<input type="text" class="js-record-data">
|
||||
<input type="text" class="js-record-ttl">
|
||||
<button type="button" class="js-record-save">save</button>
|
||||
</form>
|
||||
<form class="js-record-form-typex">
|
||||
<input type="hidden" class="js-record-id" />
|
||||
<label>typeX</label>
|
||||
<input type="number" class="js-record-type" value="0" />
|
||||
<input type="text" class="js-record-host">.<span class="js-record-zone">example.com</span>
|
||||
<input type="text" class="js-record-rr">
|
||||
<input type="text" class="js-record-ttl">
|
||||
<button type="button" class="js-record-save">save</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script src="/js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,636 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
var cache = { recordsMap: {} };
|
||||
var myZone;
|
||||
window.ADNS = { cache: cache, $qs: $qs };
|
||||
|
||||
if (!Element.prototype.matches) {
|
||||
Element.prototype.matches = Element.prototype.msMatchesSelector;
|
||||
}
|
||||
function $qs(qs, el) {
|
||||
return (el||document).querySelector(qs);
|
||||
}
|
||||
function $qsa(qs, el) {
|
||||
return Array.prototype.slice.call((el||document).querySelectorAll(qs));
|
||||
}
|
||||
function $on(selector, eventname, cb) {
|
||||
if (!$on._events[eventname]) {
|
||||
$on._events[eventname] = $on._dispatcher(eventname);
|
||||
document.addEventListener(eventname, $on._events[eventname]);
|
||||
}
|
||||
if (!$on._handlers[eventname]) {
|
||||
$on._handlers[eventname] = {};
|
||||
}
|
||||
if (!$on._handlers[eventname][selector]) {
|
||||
$on._handlers[eventname][selector] = [];
|
||||
}
|
||||
$on._handlers[eventname][selector].push(cb);
|
||||
}
|
||||
$on._events = {};
|
||||
$on._handlers = {};
|
||||
$on._dispatcher = function (eventname) {
|
||||
return function (ev) {
|
||||
//console.log('event: ' + ev.type);
|
||||
if (!$on._handlers[eventname]) {
|
||||
console.warn('no handlers for event');
|
||||
return;
|
||||
}
|
||||
var matches = Object.keys($on._handlers[eventname]).some(function (selector) {
|
||||
if (ev.target.matches(selector)) {
|
||||
$on._handlers[eventname][selector].forEach(function (cb) { cb(ev); });
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (matches) {
|
||||
console.warn("no handlers for selector");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
function verifyAuth(/*ev*/) {
|
||||
auth = $qs('input.js-jwt').value;
|
||||
return window.fetch(
|
||||
'/api/verify-auth'
|
||||
, { method: 'GET'
|
||||
, headers: new window.Headers({ 'Authorization': 'Bearer ' + auth })
|
||||
}
|
||||
).then(function (resp) {
|
||||
return resp.json().then(function (data) {
|
||||
if (data.error) {
|
||||
localStorage.removeItem('auth');
|
||||
console.error(data.error);
|
||||
window.alert('Bad HTTP Response: ' + data.error.message);
|
||||
throw new Error('Bad HTTP Response: ' + data.error.message);
|
||||
}
|
||||
|
||||
console.log('verify-auth:');
|
||||
console.log(data);
|
||||
|
||||
localStorage.setItem('auth', auth);
|
||||
changeAuth();
|
||||
|
||||
return fetchPeers(auth).then(function () {
|
||||
return fetchZones(auth);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
function fetchPeers(auth) {
|
||||
return window.fetch('/api/peers', {
|
||||
method: 'GET'
|
||||
, headers: new window.Headers({ 'Authorization': 'Bearer ' + auth })
|
||||
}).then(function (resp) {
|
||||
return resp.json().then(function (data) {
|
||||
var tpl = '';
|
||||
var el;
|
||||
cache.peers = data.peers;
|
||||
cache.peers.forEach(function (peer) {
|
||||
el = document.createElement('div');
|
||||
el.innerHTML = tpls.peer;
|
||||
console.log(el);
|
||||
console.log($qs('.js-peer-name', el));
|
||||
$qs('.js-peer-name', el).innerText = peer.name;
|
||||
$qs('.js-peer-name', el).dataset.id = peer.name;
|
||||
$qs('.js-peer', el).dataset.id = peer.name;
|
||||
$qs('.js-peer-address', el).innerText = peer.address || '';
|
||||
console.log(el.innerHTML);
|
||||
tpl += el.innerHTML;
|
||||
console.log(tpl);
|
||||
});
|
||||
$qs('.js-peer-tpl').innerHTML = tpl;
|
||||
console.log($qs('.js-peer-tpl').innerHTML);
|
||||
});
|
||||
});
|
||||
}
|
||||
function renderZones() {
|
||||
var tpl = '';
|
||||
var el;
|
||||
cache.zones.forEach(function (zone) {
|
||||
el = document.createElement('div');
|
||||
el.innerHTML = tpls.zone;
|
||||
console.log(el);
|
||||
console.log($qs('.js-zone', el));
|
||||
$qs('.js-zone-name', el).innerText = zone.name;
|
||||
$qs('.js-zone', el).dataset.id = zone.id;
|
||||
$qs('.js-zone', el).dataset.name = zone.name;
|
||||
console.log(el.innerHTML);
|
||||
tpl += el.innerHTML;
|
||||
console.log(tpl);
|
||||
});
|
||||
$qs('.js-zone-tpl').innerHTML = tpl;
|
||||
console.log($qs('.js-zone-tpl').innerHTML);
|
||||
}
|
||||
function fetchZones(auth) {
|
||||
return window.fetch('/api/zones', {
|
||||
method: 'GET'
|
||||
, headers: new window.Headers({ 'Authorization': 'Bearer ' + auth })
|
||||
}).then(function (resp) {
|
||||
return resp.json().then(function (data) {
|
||||
if (!tpls.zone) {
|
||||
tpls.zone = $qs('.js-zone-tpl').innerHTML;
|
||||
}
|
||||
cache.zones = data.zones;
|
||||
renderZones();
|
||||
});
|
||||
});
|
||||
}
|
||||
function renderRecords() {
|
||||
var tpl = '';
|
||||
var el;
|
||||
console.log('tpls.recordsMap:');
|
||||
console.log(tpls.recordsMap);
|
||||
cache.records.forEach(function (record) {
|
||||
if (record.soa) {
|
||||
return;
|
||||
}
|
||||
el = document.createElement('div');
|
||||
console.log('record.type:', record.type);
|
||||
el.innerHTML = tpls.recordsMap[record.type.toLowerCase()];
|
||||
console.log(el);
|
||||
console.log($qs('.js-record-name', el));
|
||||
Object.keys(record).sort().forEach(function (key) {
|
||||
if ('id' === key) { return; }
|
||||
var x = $qs('.js-record-' + key, el);
|
||||
if (x) {
|
||||
x.innerText = record[key];
|
||||
}
|
||||
});
|
||||
el.dataset.recordId = record.id;
|
||||
$qs('input.js-record-id', el).value = record.id;
|
||||
cache.recordsMap[record.id] = record;
|
||||
|
||||
//$qs('.js-record-type', el).innerText = record.type;
|
||||
//$qs('.js-record-name', el).innerText = record.name;
|
||||
//$qs('.js-record-address', el).innerText = record.address;
|
||||
console.log('el.innerHTML:');
|
||||
console.log(el.innerHTML);
|
||||
tpl += el.innerHTML;
|
||||
console.log(tpl);
|
||||
});
|
||||
$qs('.js-record-tpl').innerHTML = tpl;
|
||||
console.log($qs('.js-record-tpl').innerHTML);
|
||||
}
|
||||
function fetchRecords(zone) {
|
||||
return window.fetch('/api/zones/' + zone + '/records', {
|
||||
method: 'GET'
|
||||
, headers: new window.Headers({ 'Authorization': 'Bearer ' + auth })
|
||||
}).then(function (resp) {
|
||||
return resp.json().then(function (data) {
|
||||
cache.records = data.records;
|
||||
renderRecords();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// BEGIN
|
||||
var tpls = {};
|
||||
var auth = localStorage.getItem('auth');
|
||||
if (auth) {
|
||||
$qs('input.js-jwt').value = auth;
|
||||
verifyAuth();
|
||||
}
|
||||
|
||||
$on('body', 'click', function () {
|
||||
console.log('woo-hoo, that tickles my body!');
|
||||
});
|
||||
|
||||
$on('button.js-jwt', 'click', verifyAuth);
|
||||
|
||||
function changeAuth() {
|
||||
$qs('a.js-peers').href =
|
||||
$qs('a.js-peers').dataset.href.replace(/:token/, auth);
|
||||
$qs('a.js-zones').href =
|
||||
$qs('a.js-zones').dataset.href.replace(/:token/, auth);
|
||||
$qs('a.js-records').href =
|
||||
$qs('a.js-records').dataset.href.replace(/:token/, auth);
|
||||
changeZone();
|
||||
changeRecord();
|
||||
}
|
||||
function changeZone(ev) {
|
||||
var val = (ev && ev.target.value || $qs('input.js-zone').value) || ':zone';
|
||||
$qs('code.js-zone').innerHTML = val;
|
||||
// $qs('a.js-zone').setAttribute('data-href', ...)
|
||||
$qs('a.js-zone').href =
|
||||
$qs('a.js-zone').dataset.href.replace(/:zone/, val).replace(/:token/, auth);
|
||||
}
|
||||
$on('input.js-zone', 'keyup', changeZone);
|
||||
|
||||
function changeRecord(ev) {
|
||||
var val = (ev && ev.target.value || $qs('input.js-name').value) || ':name';
|
||||
$qs('code.js-name').innerHTML = val;
|
||||
$qs('a.js-name').href =
|
||||
$qs('a.js-name').dataset.href.replace(/:name/, val).replace(/:token/, auth);
|
||||
}
|
||||
$on('input.js-name', 'keyup', changeRecord);
|
||||
$on('button.js-zone-destroy', 'click', function (ev) {
|
||||
var zoneId = ev.target.parentElement.dataset.id;
|
||||
var zoneName = ev.target.parentElement.dataset.name;
|
||||
|
||||
if (!window.confirm("Remove zone '" + zoneName + "' and all associated records?")) {
|
||||
return;
|
||||
}
|
||||
|
||||
return window.fetch(
|
||||
'/api/zones/' + zoneId
|
||||
, { method: 'DELETE'
|
||||
, headers: new window.Headers({
|
||||
'Authorization': 'Bearer ' + auth
|
||||
, 'Content-Type': 'application/json;charset=UTF-8'
|
||||
})
|
||||
}
|
||||
).then(function (resp) {
|
||||
return resp.json().then(function (data) {
|
||||
var zone;
|
||||
var records = [];
|
||||
if (data.error) {
|
||||
console.error(data);
|
||||
window.alert(data.error.message);
|
||||
return;
|
||||
}
|
||||
console.log('zone undo data:');
|
||||
console.log(data);
|
||||
cache.zones.some(function (z, i) {
|
||||
if (z.id === zoneId) {
|
||||
zone = cache.zones.splice(i, 1)[0];
|
||||
return true;
|
||||
}
|
||||
});
|
||||
function removeRecord(r, i) {
|
||||
if (r.zone === zone.name || 'SOA' === r.type && r.name === zone.name) {
|
||||
records.push(cache.records.splice(i, 1)[0]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
while (cache.records.some(removeRecord)) { continue; }
|
||||
|
||||
renderZones();
|
||||
renderRecords();
|
||||
console.log('result:', data);
|
||||
});
|
||||
});
|
||||
});
|
||||
$on('button.js-zone-view', 'click', function (ev) {
|
||||
var zone = ev.target.parentElement.dataset.name;
|
||||
myZone = zone;
|
||||
return fetchRecords(zone);/*.then(function () {
|
||||
});*/
|
||||
});
|
||||
function setZoneNs(zone) {
|
||||
cache.peers.forEach(function (p, i) {
|
||||
var $vns = $qsa('.js-zone-form-tpl .js-zone-form-vns')[i];
|
||||
$qs('.js-record-zone', $vns).innerText = zone;
|
||||
if (p.address) {
|
||||
$qs('.js-record-address', $vns).value = p.address;
|
||||
}
|
||||
});
|
||||
$qs('.js-zone-form-tpl .js-recordx-primary').value = 'nsx.' + zone;
|
||||
}
|
||||
function openZoneForm(myZone, ns) {
|
||||
var d = new Date();
|
||||
var zone = {
|
||||
name: myZone.name || myZone
|
||||
, admin: myZone.admin || ('admin.' + myZone)
|
||||
, expiration: myZone.expiration || 2419200 // 4 weeks
|
||||
, minimum: myZone.expiration || 5
|
||||
, serial: myZone.serial || (d.getFullYear()
|
||||
+ '' + (d.getMonth() + 1)
|
||||
+ '' + d.getDate()
|
||||
+ '' + (Math.round(Math.random() * 99)))
|
||||
, retry: myZone.retry || 1800
|
||||
, refresh: myZone.refresh || 7200
|
||||
, ttl: myZone.ttl || 300
|
||||
};
|
||||
$qs('.js-zone-form-tpl').innerHTML = tpls.newZone;
|
||||
|
||||
ns.forEach(function () {
|
||||
$qs('.js-zone-form-vns-tpl').innerHTML += tpls.newNs;
|
||||
});
|
||||
ns.forEach(function (p, i) {
|
||||
var $vns = $qsa('.js-zone-form-tpl .js-zone-form-vns')[i];
|
||||
$qs('.js-record-host', $vns).value = 'ns' + (i + 1);
|
||||
//$qs('.js-record-name', $vns).value = 'ns' + (i + 1) + '.' + myZone;
|
||||
$qs('.js-record-zone', $vns).innerText = myZone;
|
||||
if (p.address) {
|
||||
$qs('.js-record-address', $vns).value = p.address;
|
||||
}
|
||||
$qs('.js-record-ttl', $vns).value = 7200;
|
||||
});
|
||||
|
||||
[ 'name', 'admin', 'expiration', 'minimum', 'retry', 'refresh', 'ttl' ].forEach(function (key) {
|
||||
$qs('.js-zone-form-tpl .js-record-' + key).value = zone[key];
|
||||
});
|
||||
[ 'primary', 'serial' ].forEach(function (key) {
|
||||
$qs('.js-zone-form-tpl .js-recordx-' + key).value = zone[key];
|
||||
});
|
||||
$qs('input.js-zone-form-vanityns').checked = zone.vanity;
|
||||
|
||||
$qs('input.js-zone-form-vanityns').dispatchEvent(new Event('change', { bubbles: true }));
|
||||
|
||||
console.log('val:', $qs('.js-zone-form-tpl .js-record-ttl').value);
|
||||
console.log('x', $qs('.js-zone-form-tpl'));
|
||||
console.log('val:', $qs('.js-zone-form-tpl .js-record-ttl').value);
|
||||
}
|
||||
$on('input.js-zone-form-vanityns', 'change', function (ev) {
|
||||
console.log('checked:', ev.target.checked);
|
||||
if (ev.target.checked) {
|
||||
setZoneNs(myZone);
|
||||
} else {
|
||||
setZoneNs(cache.peers[0].name.split('.').slice(1).join('.'));
|
||||
}
|
||||
});
|
||||
$on('form.js-zone-form-create', 'submit', function (ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
console.log('form submit enter');
|
||||
var myZone = $qs('.js-zone-form-name').value;
|
||||
openZoneForm(myZone, cache.peers);
|
||||
});
|
||||
$on('button.js-zone-new', 'click', function (/*ev*/) {
|
||||
//$qs('select.js-record-form-type').value = 'SOA';
|
||||
//$qs('select.js-record-form-type').dispatchEvent(new Event('change', { bubbles: true }));
|
||||
console.log('form submit click');
|
||||
var myZone = $qs('.js-zone-form-name').value;
|
||||
openZoneForm(myZone, cache.peers);
|
||||
});
|
||||
$on('button.js-zone-edit', 'click', function (ev) {
|
||||
//var zoneId = ev.target.parentElement.dataset.id;
|
||||
var zoneName = ev.target.parentElement.dataset.name;
|
||||
myZone = zoneName;
|
||||
openZoneForm(zoneName, cache.peers);
|
||||
});
|
||||
$on('button.js-zone-save', 'click', function (/*ev*/) {
|
||||
var zone = {};
|
||||
var nss = [];
|
||||
[ 'id', 'name', 'admin', 'expiration', 'minimum', 'retry', 'refresh', 'ttl' ].forEach(function (key) {
|
||||
zone[key] = $qs('.js-zone-form-tpl .js-record-' + key).value;
|
||||
});
|
||||
$qsa('.js-zone-form-tpl .js-zone-form-vns').forEach(function ($el) {
|
||||
console.log('$el:');
|
||||
console.log($el);
|
||||
var ns = {};
|
||||
[ 'id', 'host'/*, 'name'*/, 'ttl', 'address' ].forEach(function (key) {
|
||||
ns[key] = $qs('.js-record-' + key, $el).value;
|
||||
});
|
||||
nss.push(ns);
|
||||
});
|
||||
console.log('zone:');
|
||||
console.log(zone);
|
||||
console.log('nss:');
|
||||
console.log(nss);
|
||||
|
||||
zone.vanity = false;
|
||||
if ($qs('input.js-zone-form-vanityns').checked) {
|
||||
zone.vanity = true;
|
||||
zone.vanityNs = nss;
|
||||
}
|
||||
$qs('.js-zone-form-tpl').innerHTML = '';
|
||||
|
||||
return window.fetch(
|
||||
'/api/zones/' //+ zone.name
|
||||
, { method: 'POST'
|
||||
, headers: new window.Headers({
|
||||
'Authorization': 'Bearer ' + auth
|
||||
, 'Content-Type': 'application/json;charset=UTF-8'
|
||||
})
|
||||
, body: JSON.stringify(zone)
|
||||
}
|
||||
).then(function (resp) {
|
||||
return resp.json().then(function (data) {
|
||||
if (data.error) {
|
||||
console.error(data);
|
||||
window.alert(data.error.message);
|
||||
return;
|
||||
}
|
||||
console.log('result:', data);
|
||||
if (!zone.id) {
|
||||
zone.id = data.id;
|
||||
cache.zones.push(data);
|
||||
renderZones();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$on('select.js-record-form-type', 'change', function (ev) {
|
||||
var type = ev.target.value;
|
||||
var $tpl;
|
||||
console.log("form type:", type);
|
||||
$tpl = $qs('.js-record-form-tpl');
|
||||
$tpl.innerHTML = tpls.formsMap[type] || '';
|
||||
if (!tpls.formsMap[type]) {
|
||||
console.warn("no tpl for type '" + type + "'");
|
||||
return;
|
||||
}
|
||||
$qs('.js-record-type', $tpl).innerText = type;
|
||||
$qs('.js-record-zone', $tpl).innerText = myZone;
|
||||
});
|
||||
$on('button.js-record-form-new', 'click', function (/*ev*/) {
|
||||
var type = $qs('select.js-record-form-type').value;
|
||||
if (!type) {
|
||||
$qs('select.js-record-form-type').value = 'A';
|
||||
}
|
||||
$qs('select.js-record-form-type').dispatchEvent(new Event('change', { bubbles: true }));
|
||||
});
|
||||
|
||||
$on('button.js-record-edit', 'click', function (ev) {
|
||||
var id = ev.target.parentElement.querySelector('.js-record-id').value;
|
||||
var record = cache.recordsMap[id];
|
||||
var formTpl = tpls.formsMap[record.type];
|
||||
var $tpl;
|
||||
if (!formTpl) {
|
||||
formTpl = tpls.formsMap.typeX;
|
||||
}
|
||||
|
||||
console.log(ev.target);
|
||||
console.log(id);
|
||||
console.log(record);
|
||||
|
||||
formTpl = tpls.formsMap[(record.type||'typeX')];
|
||||
$qs('select.js-record-form-type').value = (record.type||'typeX');
|
||||
$qs('select.js-record-form-type').dispatchEvent(new Event('change', { bubbles: true }));
|
||||
$tpl = $qs('.js-record-form-tpl');
|
||||
$tpl.innerHTML = formTpl || '';
|
||||
|
||||
record.host = record.name.replace(new RegExp('\\.?' + (record.zone || record.name).replace(/\./g, '\\.') + '$'), '');
|
||||
|
||||
console.log('record.type:');
|
||||
console.log(record.type);
|
||||
|
||||
Object.keys(record).forEach(function (key) {
|
||||
var $el = $qs('.js-record-' + key, $tpl);
|
||||
if (!$el) {
|
||||
return;
|
||||
}
|
||||
$el.value = record[key];
|
||||
});
|
||||
if ('SOA' === record.type) {
|
||||
$qs('.js-record-name').disabled = 'disabled';
|
||||
} else {
|
||||
if (!record.host) { $qs('.js-record-host', $tpl).placeholder = '@'; }
|
||||
}
|
||||
$qs('.js-record-type', $tpl).innerHTML = record.type;
|
||||
$qs('.js-record-zone', $tpl).innerText = myZone;
|
||||
});
|
||||
|
||||
$on('button.js-record-save', 'click', function (ev) {
|
||||
console.log('save');
|
||||
var $pel = ev.target.parentElement;
|
||||
var id = $qs('.js-record-id', $pel).value;
|
||||
var existingRecord = cache.recordsMap[id];
|
||||
var record = {};
|
||||
var change;
|
||||
|
||||
$qsa('input[class*="js-record-"]', $pel).forEach(function ($el) {
|
||||
var key;
|
||||
Array.prototype.some.call($el.classList, function (str) {
|
||||
if (/^js-record-/.test(str)) {
|
||||
key = str.replace(/^js-record-(.*)/, '$1');
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (!key) { return; }
|
||||
if ('undefined' === typeof $el.value || 'INPUT' !== $el.tagName) { return; }
|
||||
record[key] = $el.value;
|
||||
});
|
||||
|
||||
if (!record.id) {
|
||||
record.id = '';
|
||||
}
|
||||
if (!record.zone) {
|
||||
record.zone = myZone;
|
||||
}
|
||||
if (!record.name) {
|
||||
if (record.host) {
|
||||
record.name = record.host + '.' + myZone;
|
||||
} else {
|
||||
record.name = myZone;
|
||||
}
|
||||
}
|
||||
|
||||
record.type = record.type || $qs('.js-record-type', $pel).innerHTML || $qs('.js-record-type', $pel).value;
|
||||
console.log('record.type:', record.type);
|
||||
/*
|
||||
if (!tpls.recordsMap[record.type.toLowerCase()]) {
|
||||
record.typex = 'typex';
|
||||
}
|
||||
*/
|
||||
|
||||
if (!existingRecord) {
|
||||
change = true;
|
||||
cache.recordsMap[record.id] = record;
|
||||
cache.records.push(record);
|
||||
} else {
|
||||
console.log('keys:', Object.keys(record));
|
||||
Object.keys(record).forEach(function (key) {
|
||||
console.log('key:', key);
|
||||
//if ('SOA' === record.type && 'zone' === key) { return; }
|
||||
if (existingRecord[key].toString() !== record[key].toString()) {
|
||||
change = true;
|
||||
existingRecord[key] = record[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!change) {
|
||||
return;
|
||||
}
|
||||
|
||||
renderRecords();
|
||||
|
||||
return window.fetch(
|
||||
'/api/records/' + record.id
|
||||
, { method: 'POST'
|
||||
, headers: new window.Headers({
|
||||
'Authorization': 'Bearer ' + auth
|
||||
, 'Content-Type': 'application/json;charset=UTF-8'
|
||||
})
|
||||
, body: JSON.stringify(record)
|
||||
}
|
||||
).then(function (resp) {
|
||||
return resp.json().then(function (data) {
|
||||
if (data.error) {
|
||||
console.error(data);
|
||||
window.alert(data.error.message);
|
||||
return;
|
||||
}
|
||||
console.log('result:', data);
|
||||
if (!record.id) {
|
||||
record.id = data.id;
|
||||
renderRecords();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$on('button.js-record-destroy', 'click', function (ev) {
|
||||
console.log('destroy');
|
||||
var $pel = ev.target.parentElement;
|
||||
var id = $qs('.js-record-id', $pel).value;
|
||||
var existingRecord = cache.recordsMap[id];
|
||||
|
||||
return window.fetch(
|
||||
'/api/records/' + id
|
||||
, { method: 'DELETE'
|
||||
, headers: new window.Headers({
|
||||
'Authorization': 'Bearer ' + auth
|
||||
, 'Content-Type': 'application/json;charset=UTF-8'
|
||||
})
|
||||
}
|
||||
).then(function (resp) {
|
||||
return resp.json().then(function (data) {
|
||||
if (data.error) {
|
||||
console.error(data);
|
||||
window.alert(data.error.message);
|
||||
return;
|
||||
}
|
||||
delete cache.recordsMap[id];
|
||||
cache.records.some(function (r, i) {
|
||||
if (r === existingRecord) {
|
||||
cache.records.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
renderRecords();
|
||||
console.log('result:', data);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
tpls.newNs = $qs('.js-zone-form-tpl .js-zone-form-vns-tpl').innerHTML;
|
||||
$qs('.js-zone-form-tpl .js-zone-form-vns-tpl').innerHTML = '';
|
||||
tpls.newZone = $qs('.js-zone-form-tpl').innerHTML;
|
||||
tpls.peer = $qs('.js-peer-tpl').innerHTML;
|
||||
$qs('.js-peer-tpl').innerHTML = '';
|
||||
tpls.formsMap = {};
|
||||
tpls.formsMap.SOA = $qs('.js-record-form-soa').outerHTML;
|
||||
tpls.formsMap.NS = $qs('.js-record-form-ns').outerHTML;
|
||||
tpls.formsMap.A = $qs('.js-record-form-a').outerHTML;
|
||||
tpls.formsMap.AAAA = $qs('.js-record-form-aaaa').outerHTML;
|
||||
tpls.formsMap.ANAME = $qs('.js-record-form-aname').outerHTML;
|
||||
tpls.formsMap.CAA = $qs('.js-record-form-caa').outerHTML;
|
||||
tpls.formsMap.CNAME = $qs('.js-record-form-cname').outerHTML;
|
||||
tpls.formsMap.MX = $qs('.js-record-form-mx').outerHTML;
|
||||
tpls.formsMap.PTR = $qs('.js-record-form-ptr').outerHTML;
|
||||
tpls.formsMap.SRV = $qs('.js-record-form-srv').outerHTML;
|
||||
tpls.formsMap.TXT = $qs('.js-record-form-txt').outerHTML;
|
||||
tpls.formsMap.typeX = $qs('.js-record-form-typex').outerHTML;
|
||||
tpls.recordsMap = {};
|
||||
tpls.recordsMap.soa = $qs('.js-record-soa').outerHTML;
|
||||
tpls.recordsMap.ns = $qs('.js-record-ns').outerHTML;
|
||||
tpls.recordsMap.a = $qs('.js-record-a').outerHTML;
|
||||
tpls.recordsMap.aaaa = $qs('.js-record-aaaa').outerHTML;
|
||||
tpls.recordsMap.aname = $qs('.js-record-aname').outerHTML;
|
||||
tpls.recordsMap.cname = $qs('.js-record-cname').outerHTML;
|
||||
tpls.recordsMap.caa = $qs('.js-record-caa').outerHTML;
|
||||
tpls.recordsMap.ptr = $qs('.js-record-ptr').outerHTML;
|
||||
tpls.recordsMap.mx = $qs('.js-record-mx').outerHTML;
|
||||
tpls.recordsMap.txt = $qs('.js-record-txt').outerHTML;
|
||||
tpls.recordsMap.srv = $qs('.js-record-srv').outerHTML;
|
||||
$qs('.js-record-tpl').innerHTML = '';
|
||||
$qs('.js-zone-form-tpl').innerHTML = '';
|
||||
$qs('select.js-record-form-type').value = '';
|
||||
// Create a new 'change' event and dispatch it.
|
||||
$qs('select.js-record-form-type').dispatchEvent(new Event('change', { bubbles: true }));
|
||||
|
||||
}());
|
|
@ -4,33 +4,484 @@ module.exports.create = function (opts) {
|
|||
// opts = { filepath };
|
||||
var engine = { db: null };
|
||||
|
||||
var db = require(opts.filepath);
|
||||
function notDeleted(r) {
|
||||
return !r.revokedAt && !r.deletedAt;
|
||||
}
|
||||
|
||||
engine.primaryNameservers = db.primaryNameservers;
|
||||
engine.getSoas = function (query, cb) {
|
||||
var myDomains = db.domains.filter(function (d) {
|
||||
return -1 !== query.names.indexOf(d.id.toLowerCase());
|
||||
});
|
||||
process.nextTick(function () {
|
||||
cb(null, myDomains);
|
||||
var db = require(opts.filepath);
|
||||
var stat = require('fs').statSync(opts.filepath);
|
||||
var crypto = require('crypto');
|
||||
//
|
||||
// Manual Migration
|
||||
//
|
||||
db.primaryNameservers.forEach(function (ns, i, arr) {
|
||||
if ('string' === typeof ns) {
|
||||
ns = { name: ns };
|
||||
arr[i] = ns;
|
||||
}
|
||||
if (!ns.id) {
|
||||
ns.id = crypto.randomBytes(16).toString('hex');
|
||||
}
|
||||
});
|
||||
db.zones = db.zones || [];
|
||||
if (db.domains) {
|
||||
db.zones = db.zones.concat(db.domains);
|
||||
}
|
||||
db.zones.forEach(function (zone) {
|
||||
if (!zone.name) {
|
||||
zone.name = zone.id;
|
||||
zone.id = null;
|
||||
}
|
||||
if (!zone.id) {
|
||||
zone.id = crypto.randomBytes(16).toString('hex');
|
||||
}
|
||||
if (!zone.createdAt) { zone.createdAt = stat.mtime.valueOf(); }
|
||||
if (!zone.updatedAt) { zone.updatedAt = stat.mtime.valueOf(); }
|
||||
});
|
||||
db.records.forEach(function (record) {
|
||||
if (!record.id) {
|
||||
record.id = crypto.randomBytes(16).toString('hex');
|
||||
}
|
||||
});
|
||||
require('fs').writeFileSync(opts.filepath, JSON.stringify(db, null, 2));
|
||||
//
|
||||
// End Migration
|
||||
//
|
||||
|
||||
db.save = function (cb) {
|
||||
if (db.save._saving) {
|
||||
console.log('make pending');
|
||||
db.save._pending.push(cb);
|
||||
return;
|
||||
}
|
||||
|
||||
db.save._saving = true;
|
||||
require('fs').writeFile(opts.filepath, JSON.stringify(db, null, 2), function (err) {
|
||||
console.log('done writing');
|
||||
var pending = db.save._pending.splice(0);
|
||||
db.save._saving = false;
|
||||
cb(err);
|
||||
if (!pending.length) {
|
||||
return;
|
||||
}
|
||||
db.save(function (err) {
|
||||
console.log('double save');
|
||||
pending.forEach(function (cb) { cb(err); });
|
||||
});
|
||||
});
|
||||
};
|
||||
engine.getRecords = function (query, cb) {
|
||||
var myRecords = db.records.slice(0).filter(function (r) {
|
||||
db.save._pending = [];
|
||||
|
||||
if ('string' !== typeof r.name) {
|
||||
return false;
|
||||
engine.primaryNameservers = db.primaryNameservers;
|
||||
engine.peers = {
|
||||
all: function (cb) {
|
||||
var dns = require('dns');
|
||||
var count = db.primaryNameservers.length;
|
||||
function gotRecord() {
|
||||
count -= 1;
|
||||
if (!count) {
|
||||
cb(null, db.primaryNameservers);
|
||||
}
|
||||
}
|
||||
function getRecord(ns) {
|
||||
dns.resolve4(ns.name, function (err, addresses) {
|
||||
console.log('ns addresses:');
|
||||
console.log(addresses);
|
||||
if (err) { console.error(err); gotRecord(); return; }
|
||||
ns.type = 'A';
|
||||
ns.address = addresses[0];
|
||||
gotRecord();
|
||||
});
|
||||
}
|
||||
db.primaryNameservers.forEach(getRecord);
|
||||
}
|
||||
};
|
||||
engine.zones = {
|
||||
_immutableKeys: [ 'id', 'name', 'primary', 'serial', 'revokedAt', 'changedAt', 'insertedAt', 'updatedAt', 'deletedAt' ]
|
||||
, _mutableKeys: [ 'admin', 'expiration', 'minimum', 'refresh', 'retry', 'ttl', 'vanity' ]
|
||||
, _dateToSerial: function (date) {
|
||||
// conventionally the format is YYYYMMDDxx,
|
||||
// but since it's an integer and I don't want to keep track of incrementing xx,
|
||||
// epoch in seconds will do
|
||||
return parseInt(Math.round(date/1000).toString().slice(-10), 10);
|
||||
}
|
||||
, _toSoa: function (domain) {
|
||||
var nameservers = domain.vanityNs || engine.primaryNameservers.map(function (n) { return n.name; });
|
||||
|
||||
var index = Math.floor(Math.random() * nameservers.length) % nameservers.length;
|
||||
var nameserver = nameservers[index];
|
||||
return {
|
||||
id: domain.id
|
||||
, name: domain.name
|
||||
, typeName: 'SOA'
|
||||
, className: 'IN'
|
||||
, ttl: domain.ttl || 60
|
||||
|
||||
// nameserver -- select an NS at random if they're all in sync
|
||||
, primary: nameserver
|
||||
, name_server: nameserver
|
||||
|
||||
// admin -- email address or domain for admin
|
||||
, admin: domain.admin || ('admin.' + domain.name)
|
||||
, email_addr: domain.admin || ('admin.' + domain.name)
|
||||
|
||||
// serial -- the version, for cache-busting of secondary nameservers. suggested format: YYYYMMDDnn
|
||||
, serial: domain.serial || engine.zones._dateToSerial(domain.updatedAt || domain.createdAt || Date.now())
|
||||
, sn: domain.serial || engine.zones._dateToSerial(domain.updatedAt || domain.createdAt || Date.now())
|
||||
|
||||
// refresh -- only used when nameservers following the DNS NOTIFY spec talk
|
||||
, refresh: domain.refresh || 1800
|
||||
, ref: domain.refresh || 1800
|
||||
|
||||
// retry -- only used when nameservers following the DNS NOTIFY spec talk
|
||||
, retry: domain.retry || 600
|
||||
, ret: domain.retry || 600
|
||||
|
||||
// expiration -- how long other nameservers should continue when the primary goes down
|
||||
, expiration: domain.expiration || 2419200 // 4 weeks
|
||||
, ex: domain.expiration || 2419200 // 4 weeks
|
||||
|
||||
// minimum -- how long to cache a non-existent domain (also the default ttl for BIND)
|
||||
, minimum: domain.minimum || 5
|
||||
, nx: domain.minimum || 5
|
||||
};
|
||||
}
|
||||
, all: function (cb) {
|
||||
process.nextTick(function () {
|
||||
cb(null, db.zones.slice(0).filter(notDeleted));
|
||||
});
|
||||
}
|
||||
, get: function (queries, cb) {
|
||||
if (!Array.isArray(queries)) {
|
||||
queries = queries.names.map(function (n) {
|
||||
return { name: n };
|
||||
});
|
||||
}
|
||||
var myDomains = db.zones.filter(function (d) {
|
||||
return queries.some(function (q) {
|
||||
return (d.name.toLowerCase() === q.name) && notDeleted(d);
|
||||
});
|
||||
});
|
||||
process.nextTick(function () {
|
||||
cb(null, myDomains);
|
||||
});
|
||||
}
|
||||
, touch: function (zone, cb) {
|
||||
var existing;
|
||||
db.zones.some(function (z) {
|
||||
if (z.id && zone.id === z.id) { existing = z; return true; }
|
||||
if (z.name && zone.name === z.name) { existing = z; return true; }
|
||||
});
|
||||
if (!existing) {
|
||||
cb(null, null);
|
||||
return;
|
||||
}
|
||||
existing.updatedAt = new Date().valueOf(); // toISOString();
|
||||
console.log('touch saving...');
|
||||
db.save(function (err) {
|
||||
cb(err, !err && existing || null);
|
||||
});
|
||||
}
|
||||
, save: function (zone, cb) {
|
||||
if (zone.id) {
|
||||
console.log('update zone!');
|
||||
engine.zones.update(zone, cb);
|
||||
} else {
|
||||
engine.zones.create(zone, cb);
|
||||
}
|
||||
}
|
||||
, update: function (zone, cb) {
|
||||
var existing;
|
||||
var dirty;
|
||||
|
||||
db.zones.some(function (z) {
|
||||
if (z.id === zone.id) {
|
||||
existing = z;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
console.log('no existing zone');
|
||||
cb(new Error("zone for '" + zone.id + "' does not exist"), null);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO use IN in masterquest (or implement OR)
|
||||
// Only return single-level wildcard?
|
||||
if (query.name === r.name || ('*.' + query.name.split('.').slice(1).join('.')) === r.name) {
|
||||
return true;
|
||||
console.log('found existing zone');
|
||||
console.log(existing);
|
||||
console.log(zone);
|
||||
Object.keys(zone).forEach(function (key) {
|
||||
if (-1 !== engine.zones._immutableKeys.indexOf(key)) { return; }
|
||||
if (existing[key] !== zone[key]) {
|
||||
dirty = true;
|
||||
console.log('existing key', key, existing[key], zone[key]);
|
||||
existing[key] = zone[key];
|
||||
}
|
||||
});
|
||||
|
||||
zone.updatedAt = new Date().valueOf(); // toISOString(); // Math.round(Date.now() / 1000);
|
||||
if (dirty) {
|
||||
zone.changedAt = zone.updatedAt;
|
||||
}
|
||||
});
|
||||
process.nextTick(function () {
|
||||
cb(null, myRecords);
|
||||
});
|
||||
|
||||
console.log('saving...');
|
||||
db.save(function (err) {
|
||||
cb(err, !err && existing || null);
|
||||
});
|
||||
}
|
||||
, create: function (zone, cb) {
|
||||
var newZone = { id: crypto.randomBytes(16).toString('hex') };
|
||||
var existing;
|
||||
var nss = [];
|
||||
|
||||
zone.name = (zone.name||'').toLowerCase();
|
||||
db.zones.some(function (z) {
|
||||
if (z.name === zone.name) {
|
||||
existing = z;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
cb(new Error("tried to create new zone, but '" + existing.name + "' already exists"));
|
||||
return;
|
||||
}
|
||||
newZone.name = zone.name;
|
||||
newZone.createdAt = Date.now();
|
||||
newZone.updatedAt = newZone.createdAt;
|
||||
|
||||
Object.keys(zone).forEach(function (key) {
|
||||
//if (-1 !== engine.zones._immutableKeys.indexOf(key)) { return; }
|
||||
if (-1 === engine.zones._mutableKeys.indexOf(key)) { return; }
|
||||
newZone[key] = zone[key];
|
||||
});
|
||||
|
||||
// TODO create NS and A records for normal and vanity nameservers
|
||||
if (zone.vanity) {
|
||||
newZone.vanity = true;
|
||||
} else {
|
||||
newZone.vanity = false;
|
||||
}
|
||||
db.primaryNameservers.forEach(function (ns, i) {
|
||||
var nsx = 'ns' + (i + 1);
|
||||
var nsZone;
|
||||
var ttl = 43200; // 12h // TODO pick a well-reasoned number
|
||||
var now = Date.now();
|
||||
|
||||
if (zone.vanity) {
|
||||
nsZone = nsx + '.' + newZone.name;
|
||||
} else {
|
||||
nsZone = ns.name;
|
||||
}
|
||||
|
||||
// NS example.com ns1.example.com 43200
|
||||
nss.push({
|
||||
id: crypto.randomBytes(16).toString('hex')
|
||||
, createdAt: Date.now()
|
||||
, updatedAt: Date.now()
|
||||
, changedAt: Date.now()
|
||||
, zone: newZone.name
|
||||
, soa: true
|
||||
, type: 'NS'
|
||||
, data: nsZone
|
||||
, name: newZone.name
|
||||
, ttl: ttl
|
||||
});
|
||||
// A ns1.example.com 127.0.0.1 43200
|
||||
nss.push({
|
||||
id: crypto.randomBytes(16).toString('hex')
|
||||
, createdAt: now
|
||||
, updatedAt: now
|
||||
, changedAt: now
|
||||
, zone: newZone.name
|
||||
, soa: true
|
||||
, type: ns.type
|
||||
, name: nsZone
|
||||
, address: ns.address
|
||||
, ttl: 43200 // 12h // TODO pick a good number
|
||||
});
|
||||
});
|
||||
|
||||
db.zones.push(newZone);
|
||||
nss.forEach(function (ns) {
|
||||
db.records.push(ns);
|
||||
});
|
||||
|
||||
console.log('[zone] [create] saving...');
|
||||
db.save(function (err) {
|
||||
cb(err, !err && newZone || null);
|
||||
});
|
||||
}
|
||||
, destroy: function (zoneId, cb) {
|
||||
var zone;
|
||||
var records;
|
||||
var now = Date.now();
|
||||
|
||||
db.zones.filter(notDeleted).some(function (z) {
|
||||
if (zoneId === z.id) {
|
||||
zone = z;
|
||||
z.deletedAt = now;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!zone) {
|
||||
process.nextTick(function () {
|
||||
cb(null, null);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
records = [];
|
||||
db.records.filter(notDeleted).forEach(function (r) {
|
||||
if (zone.name === r.zone) {
|
||||
r.deletedAt = now;
|
||||
records.push(r);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('[zone] [destroy] saving...');
|
||||
db.save(function (err) {
|
||||
zone.records = records;
|
||||
cb(err, !err && zone || null);
|
||||
});
|
||||
}
|
||||
};
|
||||
engine.records = {
|
||||
all: function (cb) {
|
||||
process.nextTick(function () {
|
||||
cb(null, db.records.slice(0).filter(notDeleted));
|
||||
});
|
||||
}
|
||||
, one: function (id, cb) {
|
||||
var myRecord;
|
||||
db.records.slice(0).some(function (r) {
|
||||
if (id && id === r.id) {
|
||||
if (notDeleted(r)) {
|
||||
myRecord = r;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
process.nextTick(function () {
|
||||
cb(null, myRecord);
|
||||
});
|
||||
}
|
||||
, get: function (query, cb) {
|
||||
var myRecords = db.records.slice(0).filter(function (r) {
|
||||
|
||||
if ('string' !== typeof r.name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO use IN in masterquest (or implement OR)
|
||||
// Only return single-level wildcard?
|
||||
if (query.name === r.name || ('*.' + query.name.split('.').slice(1).join('.')) === r.name) {
|
||||
if (notDeleted(r)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
process.nextTick(function () {
|
||||
cb(null, myRecords);
|
||||
});
|
||||
}
|
||||
, save: function (record, cb) {
|
||||
function touchZone(err, r) {
|
||||
if (err) { cb(err); }
|
||||
if (!r) { cb(null, null); }
|
||||
engine.zones.touch({ name: r.zone }, cb);
|
||||
}
|
||||
|
||||
if (record.id) {
|
||||
console.log('update record!');
|
||||
engine.records.update(record, touchZone);
|
||||
} else {
|
||||
engine.records.create(record, touchZone);
|
||||
}
|
||||
}
|
||||
, update: function (record, cb) {
|
||||
var existing;
|
||||
var dirty;
|
||||
|
||||
db.records.some(function (r) {
|
||||
if (r.id === record.id) {
|
||||
existing = r;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
console.log('no existing record');
|
||||
cb(new Error("record for '" + record.id + "' does not exist"), null);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('found existing record');
|
||||
console.log(existing);
|
||||
console.log(record);
|
||||
Object.keys(record).forEach(function (key) {
|
||||
var keys = [ 'name', 'id', 'zone', 'revokedAt', 'changedAt', 'insertedAt', 'updatedAt', 'deletedAt' ];
|
||||
if (-1 !== keys.indexOf(key)) { return; }
|
||||
if (existing[key] !== record[key]) {
|
||||
dirty = true;
|
||||
console.log(existing[key], record[key]);
|
||||
existing[key] = record[key];
|
||||
}
|
||||
});
|
||||
|
||||
record.updatedAt = new Date().valueOf(); // toISOString(); // Math.round(Date.now() / 1000);
|
||||
if (dirty) {
|
||||
record.changedAt = record.updatedAt;
|
||||
}
|
||||
|
||||
console.log('saving...');
|
||||
db.save(function (err) {
|
||||
cb(err, !err && existing || null);
|
||||
});
|
||||
}
|
||||
, create: function (record, cb) {
|
||||
var obj = { id: crypto.randomBytes(16).toString('hex') };
|
||||
console.log('found existing record');
|
||||
console.log(record);
|
||||
//var keys = [ 'name', 'id', 'zone', 'revokedAt', 'changedAt', 'insertedAt', 'updatedAt', 'deletedAt' ];
|
||||
//var okeys = [ 'name', 'zone', 'admin', 'data', 'expiration', 'minimum', 'serial', 'retry', 'refresh', 'ttl', 'type' ]; // primary
|
||||
var okeys = [ 'name', 'zone', 'type', 'data', 'class', 'ttl', 'address'
|
||||
, 'exchange', 'priority', 'port', 'value', 'tag', 'flag', 'aname' ];
|
||||
okeys.forEach(function (key) {
|
||||
if ('undefined' !== typeof record[key]) {
|
||||
obj[key] = record[key];
|
||||
}
|
||||
});
|
||||
|
||||
record.updatedAt = new Date().valueOf(); // toISOString(); // Math.round(Date.now() / 1000);
|
||||
//record.changedAt = record.updatedAt;
|
||||
record.insertedAt = record.updatedAt;
|
||||
record.createdAt = record.updatedAt;
|
||||
|
||||
console.log('saving new...');
|
||||
db.records.push(record);
|
||||
db.save(function (err) {
|
||||
cb(err, record);
|
||||
});
|
||||
}
|
||||
, destroy: function (id, cb) {
|
||||
var record;
|
||||
db.records.some(function (r/*, i*/) {
|
||||
if (id === r.id) {
|
||||
record = r;
|
||||
r.deletedAt = Date.now();
|
||||
//record = db.records.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
process.nextTick(function () {
|
||||
db.save(function (err) {
|
||||
cb(err, record);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return engine;
|
||||
|
|
|
@ -27,9 +27,10 @@ module.exports.create = function (cli, dnsd) {
|
|||
|
||||
// TODO pad two bytes for lengths
|
||||
dnsd.onMessage(nb, function (err, newAb, dbgmsg) {
|
||||
var lenbuf = Buffer.from([ newAb.length >> 8, newAb.length & 255 ]);
|
||||
// TODO XXX generate legit error packet
|
||||
if (err) { console.error("Error", err); c.end(); return; }
|
||||
|
||||
var lenbuf = Buffer.from([ newAb.length >> 8, newAb.length & 255 ]);
|
||||
console.log('TCP ' + dbgmsg);
|
||||
|
||||
c.write(lenbuf);
|
||||
|
@ -53,14 +54,15 @@ module.exports.create = function (cli, dnsd) {
|
|||
process.exit(0);
|
||||
}
|
||||
console.error("TCP Server Error:");
|
||||
console.error(err);
|
||||
console.error(err.stack);
|
||||
tcpServer.close(function () {
|
||||
setTimeout(runTcp, 1000);
|
||||
});
|
||||
//throw new Error(err);
|
||||
});
|
||||
|
||||
tcpServer.listen(cli.port, function () {
|
||||
console.log('TCP Server bound');
|
||||
console.log(tcpServer.address().address + '#' + tcpServer.address().port + ' (tcp)');
|
||||
});
|
||||
|
||||
return tcpServer;
|
||||
|
|
104
lib/udpd.js
104
lib/udpd.js
|
@ -1,58 +1,66 @@
|
|||
'use strict';
|
||||
|
||||
module.exports.create = function (cli, dnsd) {
|
||||
var server = require('dgram').createSocket({
|
||||
type: cli.udp6 ? 'udp6' : 'udp4'
|
||||
, reuseAddr: true
|
||||
});
|
||||
server.bind({
|
||||
port: cli.port
|
||||
, address: cli.address
|
||||
});
|
||||
|
||||
var handlers = {};
|
||||
handlers.onError = function (err) {
|
||||
if ('EACCES' === err.code) {
|
||||
console.error("");
|
||||
console.error("EACCES: Couldn't bind to port. You probably need to use sudo, authbind, or setcap.");
|
||||
console.error("");
|
||||
process.exit(123);
|
||||
return;
|
||||
}
|
||||
console.error("error:", err.stack);
|
||||
server.close();
|
||||
};
|
||||
|
||||
handlers.onMessage = function (nb, rinfo) {
|
||||
//console.log('[DEBUG] got a UDP message', nb.length);
|
||||
//console.log(nb.toString('hex'));
|
||||
|
||||
dnsd.onMessage(nb, function (err, newAb, dbgmsg) {
|
||||
// TODO send legit error message
|
||||
if (err) { server.send(Buffer.from([0x00])); return; }
|
||||
server.send(newAb, rinfo.port, rinfo.address, function () {
|
||||
console.log(dbgmsg, rinfo.port, rinfo.address);
|
||||
});
|
||||
function runUdp() {
|
||||
var server = require('dgram').createSocket({
|
||||
type: cli.udp6 ? 'udp6' : 'udp4'
|
||||
, reuseAddr: true
|
||||
});
|
||||
server.bind({
|
||||
port: cli.port
|
||||
, address: cli.address
|
||||
});
|
||||
};
|
||||
|
||||
handlers.onListening = function () {
|
||||
/*jshint validthis:true*/
|
||||
var server = this;
|
||||
var handlers = {};
|
||||
handlers.onError = function (err) {
|
||||
if ('EACCES' === err.code) {
|
||||
console.error("");
|
||||
console.error("EACCES: Couldn't bind to port. You probably need to use sudo, authbind, or setcap.");
|
||||
console.error("");
|
||||
process.exit(123);
|
||||
return;
|
||||
}
|
||||
console.error("UDP Server Error:");
|
||||
console.error(err.stack);
|
||||
server.close(function () {
|
||||
setTimeout(runUdp, 1000);
|
||||
});
|
||||
//throw new Error(err);
|
||||
};
|
||||
|
||||
if (cli.mdns || '224.0.0.251' === cli.nameserver) {
|
||||
server.setBroadcast(true);
|
||||
server.addMembership(cli.nameserver);
|
||||
}
|
||||
handlers.onMessage = function (nb, rinfo) {
|
||||
//console.log('[DEBUG] got a UDP message', nb.length);
|
||||
//console.log(nb.toString('hex'));
|
||||
|
||||
console.log('');
|
||||
console.log('Bound and Listening:');
|
||||
console.log(server.address().address + '#' + server.address().port + ' (' + server.type + ')');
|
||||
};
|
||||
dnsd.onMessage(nb, function (err, newAb, dbgmsg) {
|
||||
// TODO send legit error message
|
||||
if (err) { server.send(Buffer.from([0x00]), rinfo.port, rinfo.address); return; }
|
||||
server.send(newAb, rinfo.port, rinfo.address, function () {
|
||||
console.log('[dnsd.onMessage] ' + dbgmsg, rinfo.port, rinfo.address);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
server.on('error', handlers.onError);
|
||||
server.on('message', handlers.onMessage);
|
||||
server.on('listening', handlers.onListening);
|
||||
handlers.onListening = function () {
|
||||
/*jshint validthis:true*/
|
||||
var server = this;
|
||||
|
||||
return server;
|
||||
if (cli.mdns || '224.0.0.251' === cli.nameserver) {
|
||||
server.setBroadcast(true);
|
||||
server.addMembership(cli.nameserver);
|
||||
}
|
||||
|
||||
console.log('');
|
||||
console.log('Bound and Listening:');
|
||||
console.log(server.address().address + '#' + server.address().port + ' (' + server.type + ')');
|
||||
};
|
||||
|
||||
server.on('error', handlers.onError);
|
||||
server.on('message', handlers.onMessage);
|
||||
server.on('listening', handlers.onListening);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
return runUdp();
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "digd.js",
|
||||
"version": "1.2.1",
|
||||
"version": "1.3.0",
|
||||
"description": "A lightweight DNS / mDNS daemon (server) for creating and capturing DNS and mDNS query and response packets to disk as binary and/or JSON. Options are similar to the Unix dig command.",
|
||||
"main": "bin/digd.js",
|
||||
"homepage": "https://git.coolaj86.com/coolaj86/digd.js",
|
||||
|
@ -47,7 +47,11 @@
|
|||
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com)",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.18.2",
|
||||
"dig.js": "git+https://git.coolaj86.com/coolaj86/dig.js#v1.3",
|
||||
"hexdump.js": "git+https://git.coolaj86.com/coolaj86/hexdump.js#v1.0.4"
|
||||
"express": "^4.16.2",
|
||||
"hexdump.js": "git+https://git.coolaj86.com/coolaj86/hexdump.js#v1.0.4",
|
||||
"jsonwebtoken": "^8.1.0",
|
||||
"jwk-to-pem": "^1.2.6"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,86 +1,764 @@
|
|||
{ "primaryNameservers": [ "ns1.daplie.com", "ns2.daplie.com", "ns3.daplie.com" ]
|
||||
, "domains": [
|
||||
{ "id": "daplie.com", "revokedAt": 0 }
|
||||
, { "id": "daplie.domains", "revokedAt": 0, "vanityNs": [ "ns1.daplie.domains", "ns2.daplie.domains", "ns3.daplie.domains" ] }
|
||||
, { "id": "daplie.me", "revokedAt": 0, "vanityNs": [ "ns1.daplie.me", "ns2.daplie.me", "ns3.daplie.me" ] }
|
||||
, { "id": "oauth3.org", "revokedAt": 0 }
|
||||
{
|
||||
"primaryNameservers": [
|
||||
{
|
||||
"name": "ns1.daplie.com",
|
||||
"id": "65a90f49f906ee5a3823e443ce5cc9a9"
|
||||
},
|
||||
{
|
||||
"name": "ns2.daplie.com",
|
||||
"id": "5c05ac90852ee645e465da523b306800"
|
||||
},
|
||||
{
|
||||
"name": "ns3.daplie.com",
|
||||
"id": "24dd0e0881f900b0bb3ed7bf9267e8f1"
|
||||
}
|
||||
],
|
||||
"zones": [
|
||||
{
|
||||
"id": "1dd4ca0da57dbab8b536e4a4148ebf7b",
|
||||
"revokedAt": 0,
|
||||
"name": "daplie.com"
|
||||
},
|
||||
{
|
||||
"id": "b3a542f49a1cd4bc417622b9f0712400",
|
||||
"revokedAt": 0,
|
||||
"vanityNs": [
|
||||
"ns1.daplie.domains",
|
||||
"ns2.daplie.domains",
|
||||
"ns3.daplie.domains"
|
||||
],
|
||||
"name": "daplie.domains"
|
||||
},
|
||||
{
|
||||
"id": "e2cf3bc08cf090d139ea45a6542b0caa",
|
||||
"revokedAt": 0,
|
||||
"vanityNs": [
|
||||
"ns1.daplie.me",
|
||||
"ns2.daplie.me",
|
||||
"ns3.daplie.me"
|
||||
],
|
||||
"name": "daplie.me"
|
||||
},
|
||||
{
|
||||
"id": "e8ba9456b5966d22b5737fccc09a2685",
|
||||
"revokedAt": 0,
|
||||
"name": "oauth3.org"
|
||||
}
|
||||
],
|
||||
"records": [
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "daplie.com",
|
||||
"type": "NS",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "ns1",
|
||||
"data": "ns1.daplie.com",
|
||||
"id": "1d650317017daa83f493786268a267d9"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "daplie.com",
|
||||
"type": "NS",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "ns2",
|
||||
"data": "ns2.daplie.com",
|
||||
"id": "aabaa77aa289d20d6e55bcc49da2f543"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "daplie.com",
|
||||
"type": "NS",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "ns3",
|
||||
"data": "ns3.daplie.com",
|
||||
"id": "d523d2fe91ea2fc7d5f013536f583b38"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "ns1.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "ns1",
|
||||
"address": "45.55.1.122",
|
||||
"id": "19503a7b0dc6ec9bf512998fe69fa3cb"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "ns2.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "ns2",
|
||||
"address": "45.55.254.197",
|
||||
"id": "12b35252ff4a9d9b08c7824ebd20d452"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "ns3.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "ns3",
|
||||
"address": "159.203.25.112",
|
||||
"id": "1db74720ae593c3bbe171882cf34335c"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.me",
|
||||
"name": "daplie.me",
|
||||
"type": "NS",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "me",
|
||||
"sld": "daplie",
|
||||
"sub": "ns1",
|
||||
"data": "ns1.daplie.me",
|
||||
"id": "98f8f748dffc0c1025b3154d67a71bad"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.me",
|
||||
"name": "daplie.me",
|
||||
"type": "NS",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "me",
|
||||
"sld": "daplie",
|
||||
"sub": "ns2",
|
||||
"data": "ns2.daplie.me",
|
||||
"id": "0b28c6ed7f2cbe57590e673f12df2ee6"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.me",
|
||||
"name": "daplie.me",
|
||||
"type": "NS",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "me",
|
||||
"sld": "daplie",
|
||||
"sub": "ns3",
|
||||
"data": "ns3.daplie.me",
|
||||
"id": "870820f0937a0066dadc2ba283d9afcc"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.me",
|
||||
"name": "ns1.daplie.me",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "me",
|
||||
"sld": "daplie",
|
||||
"sub": "ns1",
|
||||
"address": "45.55.1.122",
|
||||
"id": "99f98a25d228108c573a91be55f90e38"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.me",
|
||||
"name": "ns2.daplie.me",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "me",
|
||||
"sld": "daplie",
|
||||
"sub": "ns2",
|
||||
"address": "45.55.254.197",
|
||||
"id": "0bbcf7056c4191f48d0c4b4394295d59"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.me",
|
||||
"name": "ns3.daplie.me",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "me",
|
||||
"sld": "daplie",
|
||||
"sub": "ns3",
|
||||
"address": "159.203.25.112",
|
||||
"id": "d017cadd989d00beab9abf855ef86df2"
|
||||
},
|
||||
{
|
||||
"zone": "oauth3.org",
|
||||
"name": "ns1.oauth3.org",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "org",
|
||||
"sld": "oauth3",
|
||||
"sub": "ns1",
|
||||
"address": "45.55.1.122",
|
||||
"id": "dbd12081d23ba12c17b0cc8c58f4a578"
|
||||
},
|
||||
{
|
||||
"zone": "oauth3.org",
|
||||
"name": "ns2.oauth3.org",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "org",
|
||||
"sld": "oauth3",
|
||||
"sub": "ns2",
|
||||
"address": "45.55.254.197",
|
||||
"id": "a6b2a9c476e20ea34c61d834303f8fff"
|
||||
},
|
||||
{
|
||||
"zone": "oauth3.org",
|
||||
"name": "ns3.oauth3.org",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "org",
|
||||
"sld": "oauth3",
|
||||
"sub": "ns3",
|
||||
"address": "159.203.25.112",
|
||||
"id": "59a041995080f04c7516a5cdc5925494"
|
||||
},
|
||||
{
|
||||
"zone": "oauth3.org",
|
||||
"name": "oauth3.org",
|
||||
"type": "NS",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "me",
|
||||
"sld": "oauth3",
|
||||
"sub": "ns1",
|
||||
"data": "ns1.oauth3.org",
|
||||
"id": "7ae22868acb69760f56a2a1b98285a19"
|
||||
},
|
||||
{
|
||||
"zone": "oauth3.org",
|
||||
"name": "oauth3.org",
|
||||
"type": "NS",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "me",
|
||||
"sld": "oauth3",
|
||||
"sub": "ns2",
|
||||
"data": "ns2.oauth3.org",
|
||||
"id": "237901ec60a8c1812250f9c389ada7b1"
|
||||
},
|
||||
{
|
||||
"zone": "oauth3.org",
|
||||
"name": "oauth3.org",
|
||||
"type": "NS",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "me",
|
||||
"sld": "oauth3",
|
||||
"sub": "ns3",
|
||||
"data": "ns3.oauth3.org",
|
||||
"id": "04bc0d1dfae48ad560bc05e60f834087"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"address": "23.228.168.108",
|
||||
"id": "52fabdc61de1e0c3e941f82354509c4d"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "daplie.com",
|
||||
"type": "TXT",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"data": [
|
||||
"v=spf1 include:mailgun.org include:spf.mandrillapp.com include:_spf.google.com include:servers.mcsv.net include:mail.zendesk.com ~all"
|
||||
],
|
||||
"id": "dee3d19cfb94616871f2dd2f20a9b025"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "www.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "www",
|
||||
"address": "23.228.168.108",
|
||||
"id": "e0cf7db3a53773e2b1b18c8c813feb7e"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "daplie.com",
|
||||
"type": "MX",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"exchange": "mxa.mailgun.org",
|
||||
"priority": 10,
|
||||
"id": "9226b3e23aeecc6e40158c1aa8f94900"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "daplie.com",
|
||||
"type": "MX",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"exchange": "mxb.mailgun.org",
|
||||
"priority": 10,
|
||||
"id": "cd4c0912c7cd6c66bc35a25c1a7fcb44"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "email.daplie.com",
|
||||
"type": "CNAME",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "email",
|
||||
"data": "mailgun.org",
|
||||
"id": "ae35a02033e64ec171ea47f6bee614a4"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "k1._domainkey.daplie.com",
|
||||
"type": "CNAME",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "k1._domainkey",
|
||||
"data": "dkim.mcsv.net",
|
||||
"id": "34a9bab7b4cf424aac48e16c9151ed28"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "smtp._domainkey.daplie.com",
|
||||
"type": "TXT",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "smtp._domainkey",
|
||||
"data": [
|
||||
"k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdEzzYX8U31O5p5Uvyb1B50/JPMcKnsnIQcPDWWYkBUQxMt+FyD1SRZLCaVxWybZ8eFQUwxlh0qFeLd/mIIGhCazQ74a3AH+TJhz4gOAvNQHmWvS0Sv9ZZjGuDM/RdOAFSwZET8+WUpJfDADfijihj5KqMab13NDDLOQ96wObuwQIDAQAB"
|
||||
],
|
||||
"id": "c421b6ec538d0155b48a3981ea1b96c4"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "mandrill._domainkey.daplie.com",
|
||||
"type": "TXT",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "mandrill._domainkey",
|
||||
"data": [
|
||||
"v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrLHiExVd55zd/IQ/J/mRwSRMAocV/hMB3jXwaHH36d9NaVynQFYV8NaWi69c1veUtRzGt7yAioXqLj7Z4TeEUoOLgrKsn8YnckGs9i3B3tVFB+Ch/4mPhXWiNfNdynHWBcPcbJ8kjEQ2U8y78dHZj1YeRXXVvWob2OaKynO8/lQIDAQAB;"
|
||||
],
|
||||
"id": "b99eaca18715483ae51602043128c3ce"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "iqqsuxwfyvyw.daplie.com",
|
||||
"type": "CNAME",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "iqqsuxwfyvyw",
|
||||
"data": "gv-roynzijsoqayyg.dv.googlehosted.com",
|
||||
"id": "e6fce430c0c4c0841c0c081b9c5a34f3"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "support.daplie.com",
|
||||
"type": "CNAME",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "support",
|
||||
"data": "daplie.zendesk.com",
|
||||
"id": "8345c4944c5b8d1c997f51f9085db1c2"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "proxy.tardigrade.devices.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "proxy.tardigrade.devices",
|
||||
"address": "23.228.168.108",
|
||||
"id": "ca8456b1003b929b734ffac3d442ffa9"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "redleader.devices.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "redleader.devices",
|
||||
"address": "104.36.98.166",
|
||||
"id": "6a78a8131a3bf053c6e3d1c9ad6b7120"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "beast.devices.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "beast.devices",
|
||||
"address": "96.19.92.42",
|
||||
"id": "7a83df9c98ea6e86574a2966846a3a80"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "ossus.devices.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "ossus.devices",
|
||||
"address": "73.65.206.97",
|
||||
"id": "b7731ad942a5051acc52054a5b228a5b"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "leo.devices.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 3600,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "leo.devices",
|
||||
"address": "45.56.59.142",
|
||||
"id": "05d05b135fa7ee4aaca663f82263035f"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "proxy.leo.devices.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 3600,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "proxy.leo.devices",
|
||||
"address": "45.56.59.142",
|
||||
"id": "7f235d22c012df06f851f4fb074dcf2d"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "git.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "git",
|
||||
"address": "23.228.168.108",
|
||||
"id": "7cc0dfd69956ac97193d4d64c7911ee1"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "tunnel.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "tunnel",
|
||||
"address": "162.243.160.23",
|
||||
"id": "19d036f38c44598e36368f50f38ca3ac"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "api.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 43200,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "api",
|
||||
"address": "23.228.168.108",
|
||||
"id": "143c7ef20ebed52fd422efa9bd8edf44"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "preorder.daplie.com",
|
||||
"type": "CNAME",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "preorder",
|
||||
"data": "daplie.myshopify.com",
|
||||
"id": "07ed749a2bb04d15c20b0df41e20e71b"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "rvpn.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "rvpn",
|
||||
"address": "104.236.182.24",
|
||||
"id": "3e57f0a99a0232e33ffc78018dfa5589"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "mailapp.daplie.com",
|
||||
"type": "CNAME",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "mailapp",
|
||||
"data": "mandrillapp.com",
|
||||
"id": "069069ef7f8b0ffd7182a55135810c3f"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "hero.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "hero",
|
||||
"address": "138.197.54.15",
|
||||
"id": "3fbc0c3d51b5ae9afe34a7d2a7f3e480"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "mattermost.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "mattermost",
|
||||
"address": "23.228.168.108",
|
||||
"id": "d4a05a3bb296a8b7e5ae579208d6a7dd"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "china-ftp.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "china-ftp",
|
||||
"address": "210.5.144.209",
|
||||
"id": "8171ab72b4244492f15105e429fb9ee4"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "shop.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "shop",
|
||||
"address": "23.227.38.32",
|
||||
"id": "4bf66b3196e658edb32b79c24555c4a3"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "shop.daplie.com",
|
||||
"type": "CNAME",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "shop",
|
||||
"data": "shops.myshopify.com",
|
||||
"id": "4d235ba90b0eac2a6cdd2ee887ece0e8"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "new.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "new",
|
||||
"address": "23.228.168.108",
|
||||
"id": "c3364d94014f3250f30810bf8ce42925"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "media.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "media",
|
||||
"address": "45.56.59.142",
|
||||
"id": "ceeb2618ebd0c8d6232f5b31f3b16eec"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "domains.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "domains",
|
||||
"address": "23.228.168.108",
|
||||
"id": "0a550bc92aa23a91b6f09dcfbb051bc7"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.com",
|
||||
"name": "labs.daplie.com",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "com",
|
||||
"sld": "daplie",
|
||||
"sub": "labs",
|
||||
"address": "23.228.168.108",
|
||||
"id": "e8ced4eb0a1ac0089e9c3f8981aab7de"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.domains",
|
||||
"name": "daplie.domains",
|
||||
"type": "NS",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "domains",
|
||||
"sld": "daplie",
|
||||
"sub": "ns1",
|
||||
"data": "ns1.daplie.domains",
|
||||
"id": "9994c44030200acc6bf32d4d1f6c8854"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.domains",
|
||||
"name": "daplie.domains",
|
||||
"type": "NS",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "domains",
|
||||
"sld": "daplie",
|
||||
"sub": "ns2",
|
||||
"data": "ns2.daplie.domains",
|
||||
"id": "3b5322d19ed697decc8ea399901925c7"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.domains",
|
||||
"name": "daplie.domains",
|
||||
"type": "NS",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "domains",
|
||||
"sld": "daplie",
|
||||
"sub": "ns3",
|
||||
"data": "ns3.daplie.domains",
|
||||
"id": "1444690ec1c527ac8894f198e9bc55a3"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.domains",
|
||||
"name": "ns1.daplie.domains",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "domains",
|
||||
"sld": "daplie",
|
||||
"sub": "ns1",
|
||||
"address": "45.55.1.122",
|
||||
"id": "ac237469a1afdef3773b14fa1280ee9e"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.domains",
|
||||
"name": "ns2.daplie.domains",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "domains",
|
||||
"sld": "daplie",
|
||||
"sub": "ns2",
|
||||
"address": "45.55.254.197",
|
||||
"id": "ac4c92727b045e40545e635f08f3e002"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.domains",
|
||||
"name": "ns3.daplie.domains",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "domains",
|
||||
"sld": "daplie",
|
||||
"sub": "ns3",
|
||||
"address": "159.203.25.112",
|
||||
"id": "96012efb05f1dea1177e8311dd8ee71a"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.domains",
|
||||
"name": "leo.devices.daplie.domains",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "domains",
|
||||
"sld": "daplie",
|
||||
"sub": "leo.devices",
|
||||
"address": "45.56.59.142",
|
||||
"id": "2c0c54b31598b769456c79f17f4c8469"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.domains",
|
||||
"name": "daplie.domains",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "domains",
|
||||
"sld": "daplie",
|
||||
"sub": "",
|
||||
"address": "45.56.59.142",
|
||||
"aname": "leo.devices.daplie.domains",
|
||||
"id": "e792d963b60c4b5eac7137513414f229"
|
||||
},
|
||||
{
|
||||
"zone": "daplie.domains",
|
||||
"name": "daplie.domains",
|
||||
"type": "A",
|
||||
"class": "IN",
|
||||
"ttl": 5,
|
||||
"tld": "domains",
|
||||
"sld": "daplie",
|
||||
"sub": "www",
|
||||
"address": "45.56.59.142",
|
||||
"aname": "leo.devices.daplie.domains",
|
||||
"id": "299767640358e61d2f95ee80fc4505bb"
|
||||
}
|
||||
]
|
||||
, "records": [
|
||||
{"zone":"daplie.com","name":"daplie.com","type":"NS","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"ns1","data":"ns1.daplie.com"}
|
||||
, {"zone":"daplie.com","name":"daplie.com","type":"NS","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"ns2","data":"ns2.daplie.com"}
|
||||
, {"zone":"daplie.com","name":"daplie.com","type":"NS","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"ns3","data":"ns3.daplie.com"}
|
||||
|
||||
, {"zone":"daplie.com","name":"ns1.daplie.com","type":"A","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"ns1","address":"45.55.1.122"}
|
||||
, {"zone":"daplie.com","name":"ns2.daplie.com","type":"A","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"ns2","address":"45.55.254.197"}
|
||||
, {"zone":"daplie.com","name":"ns3.daplie.com","type":"A","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"ns3","address":"159.203.25.112"}
|
||||
|
||||
, {"zone":"daplie.me","name":"daplie.me","type":"NS","class":"IN","ttl":43200,"tld":"me","sld":"daplie","sub":"ns1","data":"ns1.daplie.me"}
|
||||
, {"zone":"daplie.me","name":"daplie.me","type":"NS","class":"IN","ttl":43200,"tld":"me","sld":"daplie","sub":"ns2","data":"ns2.daplie.me"}
|
||||
, {"zone":"daplie.me","name":"daplie.me","type":"NS","class":"IN","ttl":43200,"tld":"me","sld":"daplie","sub":"ns3","data":"ns3.daplie.me"}
|
||||
|
||||
, {"zone":"daplie.me","name":"ns1.daplie.me","type":"A","class":"IN","ttl":5,"tld":"me","sld":"daplie","sub":"ns1","address":"45.55.1.122"}
|
||||
, {"zone":"daplie.me","name":"ns2.daplie.me","type":"A","class":"IN","ttl":5,"tld":"me","sld":"daplie","sub":"ns2","address":"45.55.254.197"}
|
||||
, {"zone":"daplie.me","name":"ns3.daplie.me","type":"A","class":"IN","ttl":5,"tld":"me","sld":"daplie","sub":"ns3","address":"159.203.25.112"}
|
||||
|
||||
, {"zone":"oauth3.org","name":"ns1.oauth3.org","type":"A","class":"IN","ttl":5,"tld":"org","sld":"oauth3","sub":"ns1","address":"45.55.1.122"}
|
||||
, {"zone":"oauth3.org","name":"ns2.oauth3.org","type":"A","class":"IN","ttl":5,"tld":"org","sld":"oauth3","sub":"ns2","address":"45.55.254.197"}
|
||||
, {"zone":"oauth3.org","name":"ns3.oauth3.org","type":"A","class":"IN","ttl":5,"tld":"org","sld":"oauth3","sub":"ns3","address":"159.203.25.112"}
|
||||
|
||||
, {"zone":"oauth3.org","name":"oauth3.org","type":"NS","class":"IN","ttl":43200,"tld":"me","sld":"oauth3","sub":"ns1","data":"ns1.oauth3.org"}
|
||||
, {"zone":"oauth3.org","name":"oauth3.org","type":"NS","class":"IN","ttl":43200,"tld":"me","sld":"oauth3","sub":"ns2","data":"ns2.oauth3.org"}
|
||||
, {"zone":"oauth3.org","name":"oauth3.org","type":"NS","class":"IN","ttl":43200,"tld":"me","sld":"oauth3","sub":"ns3","data":"ns3.oauth3.org"}
|
||||
|
||||
|
||||
|
||||
, {"zone":"daplie.com","name":"daplie.com","type":"A","class":"IN","ttl":43200,"tld":"com","sld":"daplie","address":"23.228.168.108"}
|
||||
, {"zone":"daplie.com","name":"daplie.com","type":"TXT","class":"IN","ttl":43200,"tld":"com","sld":"daplie","data":["v=spf1 include:mailgun.org include:spf.mandrillapp.com include:_spf.google.com include:servers.mcsv.net include:mail.zendesk.com ~all"]}
|
||||
, {"zone":"daplie.com","name":"www.daplie.com","type":"A","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"www","address":"23.228.168.108"}
|
||||
, {"zone":"daplie.com","name":"daplie.com","type":"MX","class":"IN","ttl":43200,"tld":"com","sld":"daplie","exchange":"mxa.mailgun.org","priority":10}
|
||||
, {"zone":"daplie.com","name":"daplie.com","type":"MX","class":"IN","ttl":43200,"tld":"com","sld":"daplie","exchange":"mxb.mailgun.org","priority":10}
|
||||
, {"zone":"daplie.com","name":"email.daplie.com","type":"CNAME","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"email","data":"mailgun.org"}
|
||||
, {"zone":"daplie.com","name":"k1._domainkey.daplie.com","type":"CNAME","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"k1._domainkey","data":"dkim.mcsv.net"}
|
||||
, {"zone":"daplie.com","name":"smtp._domainkey.daplie.com","type":"TXT","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"smtp._domainkey","data":["k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdEzzYX8U31O5p5Uvyb1B50/JPMcKnsnIQcPDWWYkBUQxMt+FyD1SRZLCaVxWybZ8eFQUwxlh0qFeLd/mIIGhCazQ74a3AH+TJhz4gOAvNQHmWvS0Sv9ZZjGuDM/RdOAFSwZET8+WUpJfDADfijihj5KqMab13NDDLOQ96wObuwQIDAQAB"]}
|
||||
, {"zone":"daplie.com","name":"mandrill._domainkey.daplie.com","type":"TXT","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"mandrill._domainkey","data":["v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrLHiExVd55zd/IQ/J/mRwSRMAocV/hMB3jXwaHH36d9NaVynQFYV8NaWi69c1veUtRzGt7yAioXqLj7Z4TeEUoOLgrKsn8YnckGs9i3B3tVFB+Ch/4mPhXWiNfNdynHWBcPcbJ8kjEQ2U8y78dHZj1YeRXXVvWob2OaKynO8/lQIDAQAB;"]}
|
||||
, {"zone":"daplie.com","name":"iqqsuxwfyvyw.daplie.com","type":"CNAME","class":"IN","ttl":5,"tld":"com","sld":"daplie","sub":"iqqsuxwfyvyw","data":"gv-roynzijsoqayyg.dv.googlehosted.com"}
|
||||
|
||||
, {"zone":"daplie.com","name":"support.daplie.com","type":"CNAME","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"support","data":"daplie.zendesk.com"}
|
||||
|
||||
, {"zone":"daplie.com","name":"proxy.tardigrade.devices.daplie.com","type":"A","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"proxy.tardigrade.devices","address":"23.228.168.108"}
|
||||
, {"zone":"daplie.com","name":"redleader.devices.daplie.com","type":"A","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"redleader.devices","address":"104.36.98.166"}
|
||||
, {"zone":"daplie.com","name":"beast.devices.daplie.com","type":"A","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"beast.devices","address":"96.19.92.42"}
|
||||
, {"zone":"daplie.com","name":"ossus.devices.daplie.com","type":"A","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"ossus.devices","address":"73.65.206.97"}
|
||||
, {"zone":"daplie.com","name":"leo.devices.daplie.com","type":"A","class":"IN","ttl":3600,"tld":"com","sld":"daplie","sub":"leo.devices","address":"45.56.59.142"}
|
||||
, {"zone":"daplie.com","name":"proxy.leo.devices.daplie.com","type":"A","class":"IN","ttl":3600,"tld":"com","sld":"daplie","sub":"proxy.leo.devices","address":"45.56.59.142"}
|
||||
|
||||
, {"zone":"daplie.com","name":"git.daplie.com","type":"A","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"git","address":"23.228.168.108"}
|
||||
, {"zone":"daplie.com","name":"tunnel.daplie.com","type":"A","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"tunnel","address":"162.243.160.23"}
|
||||
, {"zone":"daplie.com","name":"api.daplie.com","type":"A","class":"IN","ttl":43200,"tld":"com","sld":"daplie","sub":"api","address":"23.228.168.108"}
|
||||
|
||||
, {"zone":"daplie.com","name":"preorder.daplie.com","type":"CNAME","class":"IN","ttl":5,"tld":"com","sld":"daplie","sub":"preorder","data":"daplie.myshopify.com"}
|
||||
, {"zone":"daplie.com","name":"rvpn.daplie.com","type":"A","class":"IN","ttl":5,"tld":"com","sld":"daplie","sub":"rvpn","address":"104.236.182.24"}
|
||||
, {"zone":"daplie.com","name":"mailapp.daplie.com","type":"CNAME","class":"IN","ttl":5,"tld":"com","sld":"daplie","sub":"mailapp","data":"mandrillapp.com"}
|
||||
, {"zone":"daplie.com","name":"hero.daplie.com","type":"A","class":"IN","ttl":5,"tld":"com","sld":"daplie","sub":"hero","address":"138.197.54.15"}
|
||||
, {"zone":"daplie.com","name":"mattermost.daplie.com","type":"A","class":"IN","ttl":5,"tld":"com","sld":"daplie","sub":"mattermost","address":"23.228.168.108"}
|
||||
, {"zone":"daplie.com","name":"china-ftp.daplie.com","type":"A","class":"IN","ttl":5,"tld":"com","sld":"daplie","sub":"china-ftp","address":"210.5.144.209"}
|
||||
, {"zone":"daplie.com","name":"shop.daplie.com","type":"A","class":"IN","ttl":5,"tld":"com","sld":"daplie","sub":"shop","address":"23.227.38.32"}
|
||||
, {"zone":"daplie.com","name":"shop.daplie.com","type":"CNAME","class":"IN","ttl":5,"tld":"com","sld":"daplie","sub":"shop","data":"shops.myshopify.com"}
|
||||
, {"zone":"daplie.com","name":"new.daplie.com","type":"A","class":"IN","ttl":5,"tld":"com","sld":"daplie","sub":"new","address":"23.228.168.108"}
|
||||
, {"zone":"daplie.com","name":"media.daplie.com","type":"A","class":"IN","ttl":5,"tld":"com","sld":"daplie","sub":"media","address":"45.56.59.142"}
|
||||
, {"zone":"daplie.com","name":"domains.daplie.com","type":"A","class":"IN","ttl":5,"tld":"com","sld":"daplie","sub":"domains","address":"23.228.168.108"}
|
||||
, {"zone":"daplie.com","name":"labs.daplie.com","type":"A","class":"IN","ttl":5,"tld":"com","sld":"daplie","sub":"labs","address":"23.228.168.108"}
|
||||
|
||||
|
||||
|
||||
, {"zone":"daplie.domains","name":"daplie.domains","type":"NS","class":"IN","ttl":5,"tld":"domains","sld":"daplie","sub":"ns1","data":"ns1.daplie.domains"}
|
||||
, {"zone":"daplie.domains","name":"daplie.domains","type":"NS","class":"IN","ttl":5,"tld":"domains","sld":"daplie","sub":"ns2","data":"ns2.daplie.domains"}
|
||||
, {"zone":"daplie.domains","name":"daplie.domains","type":"NS","class":"IN","ttl":5,"tld":"domains","sld":"daplie","sub":"ns3","data":"ns3.daplie.domains"}
|
||||
|
||||
, {"zone":"daplie.domains","name":"ns1.daplie.domains","type":"A","class":"IN","ttl":5,"tld":"domains","sld":"daplie","sub":"ns1","address":"45.55.1.122"}
|
||||
, {"zone":"daplie.domains","name":"ns2.daplie.domains","type":"A","class":"IN","ttl":5,"tld":"domains","sld":"daplie","sub":"ns2","address":"45.55.254.197"}
|
||||
, {"zone":"daplie.domains","name":"ns3.daplie.domains","type":"A","class":"IN","ttl":5,"tld":"domains","sld":"daplie","sub":"ns3","address":"159.203.25.112"}
|
||||
|
||||
, {"zone":"daplie.domains","name":"leo.devices.daplie.domains","type":"A","class":"IN","ttl":5,"tld":"domains","sld":"daplie","sub":"leo.devices","address":"45.56.59.142"}
|
||||
, {"zone":"daplie.domains","name":"daplie.domains","type":"A","class":"IN","ttl":5,"tld":"domains","sld":"daplie","sub":"","address":"45.56.59.142","aname":"leo.devices.daplie.domains"}
|
||||
, {"zone":"daplie.domains","name":"daplie.domains","type":"A","class":"IN","ttl":5,"tld":"domains","sld":"daplie","sub":"www","address":"45.56.59.142","aname":"leo.devices.daplie.domains"}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
module.exports =
|
||||
{
|
||||
"primaryNameservers": [ "localhost" ] // 'ns1.vanity-dns.org'
|
||||
, "domains": [
|
||||
"primaryNameservers": [ { "name": "localhost" } ] // 'ns1.vanity-dns.org'
|
||||
, "zones": [
|
||||
{ "id": "example.com", "revokedAt": 0 }
|
||||
, { "id": "smith.example.com", "revokedAt": 0 }
|
||||
, { "id": "in-delegated.example.com", "revokedAt": 0 }
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"primaryNameservers": [ "localhost" ]
|
||||
, "domains": [
|
||||
"primaryNameservers": [
|
||||
{ "name": "localhost" }
|
||||
]
|
||||
, "zones": [
|
||||
{ "id": "example.com", "revokedAt": 0 }
|
||||
, { "id": "smith.example.com", "revokedAt": 0 }
|
||||
, { "id": "in-delegated.example.com", "revokedAt": 0 }
|
||||
|
|
Loading…
Reference in New Issue