Skip to content

Commit

Permalink
Merge PR #673 from 'buffrr/dnssec-synth'
Browse files Browse the repository at this point in the history
  • Loading branch information
pinheadmz committed Dec 23, 2021
2 parents 1568a5c + d9a26cd commit 06aabff
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 18 deletions.
6 changes: 6 additions & 0 deletions lib/dns/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ exports.TYPE_MAP_NS = Buffer.from('0006200000000003', 'hex');
// TXT RRSIG NSEC
exports.TYPE_MAP_TXT = Buffer.from('0006000080000003', 'hex');

// A RRSIG NSEC
exports.TYPE_MAP_A = Buffer.from('0006400000000003', 'hex');

// AAAA RRSIG NSEC
exports.TYPE_MAP_AAAA = Buffer.from('0006000000080003', 'hex');

exports.hsTypes = {
DS: 0,
NS: 1,
Expand Down
31 changes: 29 additions & 2 deletions lib/dns/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ const {
DEFAULT_TTL,
TYPE_MAP_ROOT,
TYPE_MAP_EMPTY,
TYPE_MAP_NS
TYPE_MAP_NS,
TYPE_MAP_A,
TYPE_MAP_AAAA
} = require('./common');

const {
Expand Down Expand Up @@ -321,15 +323,40 @@ class RootServer extends DNSServer {
// TLD '._synth' is being queried on its own, send SOA
// so recursive asks again with complete synth record.
if (labels.length === 1) {
// Empty non-terminal proof:
res.authority.push(
nsec.create(
'_synth.',
'\\000._synth.',
TYPE_MAP_EMPTY
)
);
key.signZSK(res.authority, types.NSEC);

res.authority.push(this.toSOA());
key.signZSK(res.authority, types.SOA);

return res;
}

const hash = util.label(name, labels, -2);
const ip = IP.map(base32.decodeHex(hash.substring(1)));
const synthType = IP.isIPv4(ip) ? types.A : types.AAAA;

// Query must be for the correct synth version
if (type !== synthType) {
// SYNTH4/6 proof:
const typeMap = synthType === types.A ? TYPE_MAP_A : TYPE_MAP_AAAA;
res.authority.push(nsec.create(name, '\\000.' + name, typeMap));
key.signZSK(res.authority, types.NSEC);

res.authority.push(this.toSOA());
key.signZSK(res.authority, types.SOA);

return res;
}

if (IP.isIPv4(ip)) {
if (synthType === types.A) {
rr.type = types.A;
rr.data = new ARecord();
} else {
Expand Down
82 changes: 66 additions & 16 deletions test/ns-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ const {
TYPE_MAP_ROOT,
TYPE_MAP_EMPTY,
TYPE_MAP_NS,
TYPE_MAP_TXT
TYPE_MAP_TXT,
TYPE_MAP_A,
TYPE_MAP_AAAA
} = require('../lib/dns/common');

describe('RootServer', function() {
Expand Down Expand Up @@ -63,8 +65,9 @@ describe('RootServer', function() {

it('should resolve a SYNTH4', async () => {
const name = '_fs0000g._synth.';
const type = wire.types.A;
const req = {
question: [{name}]
question: [{name, type}]
};

const res = await ns.resolve(req);
Expand All @@ -78,8 +81,9 @@ describe('RootServer', function() {

it('should resolve a SYNTH6', async () => {
const name = '_00000000000000000000000008._synth.';
const type = wire.types.AAAA;
const req = {
question: [{name}]
question: [{name, type}]
};

const res = await ns.resolve(req);
Expand All @@ -99,27 +103,22 @@ describe('RootServer', function() {

// Query a record the RootResolver knows even without a database
let name = '.';
let type = wire.types.NS;
let req = {
question: [
{
name,
type: wire.types.NS
}
]
question: [{name, type}]
};
let res = await ns.resolve(req);
await ns.resolve(req);

// Added to cache
assert.strictEqual(cache.size, 1);

// Query a SYNTH6 record
name = '_00000000000000000000000008._synth.';
type = wire.types.AAAA;
req = {
question: [
{name}
]
question: [{name, type}]
};
res = await ns.resolve(req);
let res = await ns.resolve(req);
let answer = res.answer;
let rec = answer[0];

Expand All @@ -136,8 +135,9 @@ describe('RootServer', function() {
// This SYNTH4 request would return the result of the SYNTH6
// record from the last request.
name = '_fs0000g._synth.';
req = {
question: [{name}]
type = wire.types.A;
req = {
question: [{name, type}]
};

res = await ns.resolve(req);
Expand Down Expand Up @@ -370,6 +370,12 @@ describe('RootServer DNSSEC', function () {
wire.types.RRSIG,
wire.types.NSEC]],
[TYPE_MAP_TXT, [wire.types.TXT,
wire.types.RRSIG,
wire.types.NSEC]],
[TYPE_MAP_A, [wire.types.A,
wire.types.RRSIG,
wire.types.NSEC]],
[TYPE_MAP_AAAA, [wire.types.AAAA,
wire.types.RRSIG,
wire.types.NSEC]]
];
Expand Down Expand Up @@ -609,6 +615,50 @@ describe('RootServer DNSSEC', function () {
assert.strictEqual(proof.data.typeBitmap, query.bitmap);
}
});

// Synth records:
it('should prove _synth as an empty non-terminal', async () => {
const queries = [
{name: '_synth.', type: wire.types.NS},
{name: '_synth.', type: wire.types.DS},
{name: '_synth.', type: wire.types.TXT}
];

for (const query of queries) {
const res = await resolve(query.name, query.type);
assert(res.aa);
assert.strictEqual(res.code, wire.codes.NOERROR);
assert.strictEqual(res.answer.length, 0);
assert(util.hasType(res.authority, wire.types.SOA));
assert(util.hasType(res.authority, wire.types.NSEC));

const set = util.extractSet(res.authority, query.name, wire.types.NSEC);
assert.strictEqual(set.length, 1);
assert.strictEqual(set[0].data.typeBitmap, TYPE_MAP_EMPTY);
}
});

it('should prove non-existence of a type for _synth records', async () => {
const queries = [
{name: '_040g208._synth.', type: wire.types.AAAA, bitmap: TYPE_MAP_A},
{name: '_040g208._synth.', type: wire.types.TXT, bitmap: TYPE_MAP_A},
{name: '_4o34e027000000000000000h24._synth.', type: wire.types.A, bitmap: TYPE_MAP_AAAA}
];

for (const query of queries) {
const res = await resolve(query.name, query.type);
assert(res.aa);
assert.strictEqual(res.code, wire.codes.NOERROR);
assert.strictEqual(res.answer.length, 0);
assert.strictEqual(res.additional.length, 0);
assert(util.hasType(res.authority, wire.types.SOA));

const set = util.extractSet(res.authority, query.name, wire.types.NSEC);
assert.strictEqual(set.length, 1);
const proof = set[0];
assert.strictEqual(proof.data.typeBitmap, query.bitmap);
}
});
});

describe('RootServer SIG0', function() {
Expand Down

0 comments on commit 06aabff

Please sign in to comment.