Skip to content

Commit

Permalink
Merge f44373d into 9f0361f
Browse files Browse the repository at this point in the history
  • Loading branch information
buffrr committed Apr 15, 2021
2 parents 9f0361f + f44373d commit d1545bd
Show file tree
Hide file tree
Showing 7 changed files with 723 additions and 49 deletions.
82 changes: 82 additions & 0 deletions lib/dns/nsec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
'use strict';

const assert = require('bsert');
const {wire, util} = require('bns');
const {Record, NSECRecord, types} = wire;

// NS SOA RRSIG NSEC DNSKEY
// Types available for the root "."
const TYPE_MAP_ROOT = Buffer.from('000722000000000380', 'hex');

// RRSIG NSEC
const TYPE_MAP_EMPTY = Buffer.from('0006000000000003', 'hex');

// NS RRSIG NSEC
const TYPE_MAP_NS = Buffer.from('0006200000000003', 'hex');

// TXT RRSIG NSEC
const TYPE_MAP_TXT = Buffer.from('0006000080000003', 'hex');

function create(name, nextDomain, typeBitmap) {
const rr = new Record();
const rd = new NSECRecord();
rr.name = util.fqdn(name);
rr.type = types.NSEC;
rr.ttl = 86400;

rd.nextDomain = util.fqdn(nextDomain);
rd.typeBitmap = typeBitmap;
rr.data = rd;

return rr;
}

// find the successor of a top level name
function nextName(tld) {
tld = util.trimFQDN(tld.toLowerCase());

// if the label is already 63 octets
// increment last character by one
if(tld.length === 63) {
// assuming no escaped octets are present
const next = Buffer.from(tld, 'ascii');
next[next.length-1]++;
return next.toString() + '.';
}

return tld + '\\000.';
}

// find the predecessor of a top level name
function prevName(tld) {
tld = util.trimFQDN(tld.toLowerCase());
assert(tld.length !== 0);

// decrement the last character by 1
// assuming no escaped octets are present
let prev = Buffer.from(tld, 'ascii');
prev[prev.length-1]--;
prev = prev.toString();

// See RFC4034 6.1 Canonical DNS Name Order
// https://tools.ietf.org/html/rfc4034#section-6.1
// Appending \255 prevents names that begin
// with the decremented name from falling
// in range i.e if the name is `hello` a lexically
// smaller name is `helln` append `\255`
// to ensure that helln\255 > hellna
// while keeping helln\255 < hello
if (prev.length < 63) {
prev += '\\255';
}

return util.fqdn(prev);
}

exports.TYPE_MAP_ROOT = TYPE_MAP_ROOT;
exports.TYPE_MAP_EMPTY = TYPE_MAP_EMPTY;
exports.TYPE_MAP_NS = TYPE_MAP_NS;
exports.TYPE_MAP_TXT = TYPE_MAP_TXT;
exports.create = create;
exports.prevName = prevName;
exports.nextName = nextName;
49 changes: 35 additions & 14 deletions lib/dns/resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const base32 = require('bcrypto/lib/encoding/base32');
const {IP} = require('binet');
const bio = require('bufio');
const key = require('./key');
const nsec = require('./nsec');
const {Struct} = bio;

const {
Expand All @@ -37,6 +38,12 @@ const {
types
} = wire;

const {
TYPE_MAP_EMPTY,
TYPE_MAP_NS,
TYPE_MAP_TXT
} = nsec;

/*
* Constants
*/
Expand Down Expand Up @@ -273,31 +280,49 @@ class Resource extends Struct {
return zone;
}

toReferral(name) {
toReferral(name, type, tld) {
const res = new Message();

if (this.hasNS()) {
// no DS referrals for TLDs
const badReferral = tld && type === types.DS;

if (this.hasNS() && !badReferral) {
res.authority = [
...this.toNS(name),
...this.toDS(name)
];

res.additional = this.toGlue(name);

// Note: should have nsec unsigned zone proof.
if (this.hasDS()) {
// DS records are the only record type
// the root zone is actually authoritative over.
res.aa = true;
key.signZSK(res.authority, types.DS);
} else {
// unsigned zone proof
res.authority.push(this.toNSEC(name));
key.signZSK(res.authority, types.NSEC);
}
} else {
// Needs SOA.
res.aa = true;
// negative answer proof
res.authority.push(this.toNSEC(name));
key.signZSK(res.authority, types.NSEC);
}

return res;
}

toNSEC(name) {
let typeMap = TYPE_MAP_EMPTY;

if(this.hasNS())
typeMap = TYPE_MAP_NS;
else if (this.hasType(hsTypes.TXT))
typeMap = TYPE_MAP_TXT;

return nsec.create(name, nsec.nextName(name), typeMap);
}

toDNS(name, type) {
assert(util.isFQDN(name));
assert((type >>> 0) === type);
Expand All @@ -307,7 +332,7 @@ class Resource extends Struct {
// Referral.
if (labels.length > 1) {
const tld = util.from(name, labels, -1);
return this.toReferral(tld);
return this.toReferral(tld, type, false);
}

// Potentially an answer.
Expand All @@ -318,10 +343,6 @@ class Resource extends Struct {
// and therefore are not signed by the root ZSK.
// The only records root is authoritative over is DS.
switch (type) {
case types.NS:
res.authority = this.toNS(name);
res.additional = this.toGlue(name);
break;
case types.TXT:
if (!this.hasNS()) {
res.aa = true;
Expand All @@ -336,10 +357,10 @@ class Resource extends Struct {
break;
}

// Nope, we need a referral.
// Nope, we may need a referral
if (res.answer.length === 0
&& res.authority.length === 0) {
return this.toReferral(name);
&& res.authority.length === 0) {
return this.toReferral(name, type, true);
}

return res;
Expand Down

0 comments on commit d1545bd

Please sign in to comment.