From a089bfc8e549a6769e2894f9837f76b4f09d1f1d Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 12 Oct 2017 12:48:09 -0600 Subject: [PATCH] add ANAME resolution --- README.md | 9 ++++++++ TESTS.md | 8 +++++++ bin/digd.js | 6 ++++++ lib/dns-store.js | 56 ++++++++++++++++++++++++++++++++++++++++++++---- samples/db.js | 3 +++ 5 files changed, 78 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 95cd154..80c4699 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,7 @@ module.exports = { // A, AAAA , address: '127.0.0.1' + , aname: 'some-device.example.com' // See "A Note on ANAMEs" below // CAA , flag: 0 @@ -194,6 +195,14 @@ Note: Because it's possible to that delegated subdomains could have delegated su original nameserver, **NS** records will be replaced with an SOA record if any of the NS records match any of the server's primary nameservers or if vanity nameservers are used. +### A Note on ANAMES + +ANAMEs serve two purposes in this system: + +1. Traditional ANAME. Just a CNAME that is automatically resolved to an A record for the "bare domain" problem, and efficiency. +2. Dynamic DNS. When a record on the system is updated, any records that match it by ANAME are also updated + +TODO: use dns0x20 for ANAME resolutions Other Resources --------------- diff --git a/TESTS.md b/TESTS.md index 27fa1fd..8cffd36 100644 --- a/TESTS.md +++ b/TESTS.md @@ -23,8 +23,16 @@ Send malformed packets (both as queries and as answers): - compression pointers to wrong bits (throw error on non-ascii / unsafe chars) Test that ANY queries return records of all types matching the domain + Test that A queries only return A records, not others matching the domain +Test that A queries for ANAME-enabled records (but no address) recurse (regardless of general recursion settings). + * 0-anames.example.com + * 1-aname.example.com + * 2-anames.example.com + +Generally speaking test the cases of 0, 1, and 2 records of any given type (null case, single case, multi case) + ``` # Sample Data: diff --git a/bin/digd.js b/bin/digd.js index 727102c..c7baad0 100755 --- a/bin/digd.js +++ b/bin/digd.js @@ -367,6 +367,12 @@ 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(); + return; + } + require('../lib/dns-store').query(path.resolve(cli.input), query, function (err, resp) { if (err) { console.log('[DEV] answer not found in local db, recursing'); console.error(err); recurse(); return; } diff --git a/lib/dns-store.js b/lib/dns-store.js index 738ab38..b42b30a 100644 --- a/lib/dns-store.js +++ b/lib/dns-store.js @@ -11,7 +11,12 @@ var NXDOMAIN = 3; var REFUSED = 5; function getRecords(db, qname, cb) { - var myRecords = db.records.filter(function (r) { + var delMe = {}; + var dns = require('dns'); + // SECURITY XXX TODO var dig = require('dig.js/dns-request'); + var count; + var myRecords = db.records.slice(0).filter(function (r) { + if ('string' !== typeof r.name) { return false; } @@ -23,9 +28,52 @@ function getRecords(db, qname, cb) { } }); - process.nextTick(function () { - cb(null, myRecords); + function checkCount() { + count -= 1; + if (count <= 0) { + myRecords = myRecords.filter(function (r) { + return !delMe[r.id]; + }); + cb(null, myRecords); + } + } + + function getRecord(r) { + // 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)]; + } else { + r._address = addresses[0]; + } + checkCount(); + }; + } + + count = myRecords.length; + myRecords.forEach(function (r) { + if (r.aname && !r.address) { + if ('A' === r.type) { + // SECURITY XXX TODO dig.resolveJson(query, opts); + dns.resolve4(r.aname, getRecord(r)); + return; + } + if ('AAAA' === r.type) { + // SECURITY XXX TODO dig.resolveJson(query, opts); + dns.resolve6(r.aname, getRecord(r)); + return; + } + } + + checkCount(); }); + + if (!myRecords.length) { + checkCount(); + } } function dbToResourceRecord(r) { @@ -47,7 +95,7 @@ function dbToResourceRecord(r) { */ // A, AAAA - , address: -1 !== [ 'A', 'AAAA' ].indexOf(r.type) ? (r.address || r.value) : undefined + , address: -1 !== [ 'A', 'AAAA' ].indexOf(r.type) ? (r._address || r.address || r.value) : undefined // CNAME, NS, PTR || TXT , data: -1 !== [ 'CNAME', 'NS', 'PTR', 'TXT' ].indexOf(r.type) ? (r.data || r.value || r.values) : undefined diff --git a/samples/db.js b/samples/db.js index 46d3db9..9b328ea 100644 --- a/samples/db.js +++ b/samples/db.js @@ -15,6 +15,9 @@ module.exports = { , { "zone": "daplie.me", "name": "www.daplie.me", "tld": "me", "sld": "daplie", "sub": "www" , "type": "A", "address": "23.228.168.108", "aname": "tardigrade.devices.daplie.me" } + , { "zone": "daplie.me", "name": "aname.daplie.me", "tld": "me", "sld": "daplie", "sub": "aname" + , "type": "A", "aname": "google.com" } + , { "zone": "daplie.me", "name": "email.daplie.me", "tld": "me", "sld": "daplie", "sub": "email" , "type": "CNAME", "data": "mailgun.org" }