Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

On the fly signature for MX #1

Open
wants to merge 11 commits into
base: cname1
Choose a base branch
from
5 changes: 5 additions & 0 deletions lib/server/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ class AuthServer extends DNSServer {
this.initOptions(options);
}

setZSKFromString(str) {
this.zone.setZSKFromString(str);
return this;
}

setOrigin(name) {
this.zone.setOrigin(name);
return this;
Expand Down
191 changes: 136 additions & 55 deletions lib/zone.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ const fs = require('bfile');
const constants = require('./constants');
const util = require('./util');
const wire = require('./wire');
const dnssec = require('./dnssec');
const {keyFlags} = dnssec;
const {ZONE} = keyFlags;


const {
types,
Expand Down Expand Up @@ -46,8 +50,10 @@ class Zone {
this.origin = '.';
this.count = 0;
this.names = new Map();
this.wild = new RecordMap();
this.wild = new RecordMap(this);
this.nsec = new NameList();
this.zskpriv = null;
this.zskkey = null;
this.setOrigin(origin);
}

Expand All @@ -65,6 +71,12 @@ class Zone {
return this;
}

setZSKFromString(str) {
const [alg, zskpriv] = dnssec.decodePrivate(str);
this.zskpriv = zskpriv;
this.zskkey = dnssec.makeKey(this.origin, alg, zskpriv, ZONE);
}

setOrigin(origin) {
if (origin == null)
origin = '.';
Expand Down Expand Up @@ -95,7 +107,7 @@ class Zone {
this.wild.insert(rr);
} else {
if (!this.names.has(rr.name))
this.names.set(rr.name, new RecordMap());
this.names.set(rr.name, new RecordMap(this));

const map = this.names.get(rr.name);

Expand All @@ -121,8 +133,8 @@ class Zone {

if (map)
map.push(name, type, an);

this.wild.push(name, type, an);
else
this.wild.push(name, type, an);

return this;
}
Expand All @@ -145,27 +157,42 @@ class Zone {
return map.rrs.has(type);
}

glue(name, an) {
glue(name, an, type, ns) {
assert(util.isFQDN(name));
assert(Array.isArray(an));

this.push(name, types.A, an);
this.push(name, types.AAAA, an);
const initial = an.length;

if (!type) {
this.push(name, types.A, an);
this.push(name, types.AAAA, an);
} else {
this.push(name, type, an);
}

const final = an.length;

// If the only answer we have is a CNAME with no "glue",
// include an SOA in the authority section, just like
// if we had no answer for a name we're authoritative over.
if (initial === final)
this.push(name, types.SOA, ns);

return this;
}

find(name, type) {
const an = this.get(name, type);
const ar = [];
const ns = [];

for (const rr of an) {
switch (rr.type) {
case types.CNAME:
this.glue(rr.data.target, an);
this.glue(rr.data.target, an, type, ns);
break;
case types.DNAME:
this.glue(rr.data.target, an);
this.glue(rr.data.target, an, type, ns);
break;
case types.NS:
this.glue(rr.data.ns, ar);
Expand All @@ -174,15 +201,15 @@ class Zone {
this.glue(rr.data.ns, ar);
break;
case types.MX:
this.glue(rr.data.mx, ar);
this.glue(util.fqdn(rr.data.mx), ar, type, ns);
break;
case types.SRV:
this.glue(rr.data.target, ar);
break;
}
}

return [an, ar];
return [an, ar, ns];
}

getHints() {
Expand Down Expand Up @@ -230,12 +257,17 @@ class Zone {
assert(util.isFQDN(name));
assert((type & 0xffff) === type);

const [an, ar] = this.find(name, type);
const labels = util.split(name);
const zone = util.from(name, labels, -this.count);
const authority = util.equal(zone, this.origin);

let [an, ar, ns] = this.find(name, type);
let glue;

// Do we have an answer?
if (an.length > 0) {
// Are we authoritative for this name?
if (!this.has(name, types.SOA)) {
if (!authority) {
// If we're not authoritative for this
// name, this is probably a request
// for a DS or NSEC record.
Expand All @@ -246,34 +278,17 @@ class Zone {
return [[], an, ar, false, true];
}

// Send the answer but do
// not set the `aa` bit.
return [an, [], ar, false, true];
}

// We're authoritative. Send the
// answer and set the `aa` bit.
return [an, [], ar, true, true];
}

const labels = util.split(name);

// Are they requesting a child of our
// origin? If not, handle the mishap
// gracefully.
if (this.origin !== '.') {
const zone = util.from(name, labels, -this.count);

// Refer them back to the root zone.
if (!util.equal(zone, this.origin)) {
const [ns, ar] = this.getHints();
return [[], ns, ar, false, true];
}
return [an, ns, ar, true, true];
}

// Couldn't find anything.
// Serve an SoA (no data).
if (labels.length === this.count) {
if (authority) {
const ns = this.get(this.origin, types.SOA);
this.proveNoData(ns);
return [[], ns, [], true, false];
Expand All @@ -284,13 +299,18 @@ class Zone {
// might have a referral for.
const index = this.count + 1;
const child = util.from(name, labels, -index);
const [ns, glue] = this.find(child, types.NS);
[ns, glue] = this.find(child, types.NS);

// Couldn't find any nameservers.
// Serve an SoA (nxdomain).
if (ns.length === 0) {
const ns = this.get(this.origin, types.SOA);
this.proveNameError(child, ns);
let ns = [];
// The root zone can prove the TLD doesn't exist with authority
// but regular authoritative name servers should be as quiet as possible.
if (this.origin === '.') {
ns = this.get(this.origin, types.SOA);
this.proveNameError(child, ns);
}
return [[], ns, [], false, false];
}

Expand Down Expand Up @@ -348,11 +368,12 @@ class Zone {
*/

class RecordMap {
constructor() {
constructor(zone) {
// type -> rrs
this.rrs = new Map();
// type covered -> sigs
this.sigs = new Map();
this.zone = zone;
}

clear() {
Expand Down Expand Up @@ -388,24 +409,94 @@ class RecordMap {
return this;
}

filterMatches(name, rrs) {
const ret = [];

for (const rr of rrs) {
if (!isWild(rr.name)) {
ret.push(rr);
continue;
}

const x = util.splitName(name);
const y = util.splitName(rr.name);

if (x.length < y.length)
continue;

// Remove '*' label and test remainder
y.shift();

let push = true;
for (let i = 1; i <= y.length; i++) {
if (y[y.length - i] !== x[x.length - i]) {
push = false;
break;
}
}
if (!push)
continue;

ret.push(rr);
}

return ret;
}

push(name, type, an) {
assert(util.isFQDN(name));
assert((type & 0xffff) === type);
assert(Array.isArray(an));

const rrs = this.rrs.get(type);
// If a name has a CNAME record, there should be no
// other records for that name in the zone.
// (RFC 1034 section 3.6.2, RFC 1912 section 2.4)
if (type !== types.CNAME) {
let rrs = this.rrs.get(types.CNAME);

if (!rrs || rrs.length === 0)
return this;
if (rrs && rrs.length > 0) {
rrs = this.filterMatches(name, rrs);
for (const rr of rrs)
an.push(convert(name, rr));

for (const rr of rrs)
an.push(convert(name, rr));
let sigs = this.sigs.get(types.CNAME);

const sigs = this.sigs.get(type);
if (sigs) {
sigs = this.filterMatches(name, sigs);
for (const rr of sigs)
an.push(convert(name, rr));
}

if (sigs) {
for (const rr of sigs)
if (!sigs && this.zone.zskkey && this.zone.zskpriv) {
// Create dnssec sig on the fly (especially useful for wildcard)
const sig = dnssec.sign(this.zone.zskkey, this.zone.zskpriv, an);
an.push(sig);
}

return this;
}
}

let rrs = this.rrs.get(type);

if (rrs && rrs.length > 0) {
rrs = this.filterMatches(name, rrs);
for (const rr of rrs)
an.push(convert(name, rr));

let sigs = this.sigs.get(type);

if (sigs) {
sigs = this.filterMatches(name, sigs);
for (const rr of sigs)
an.push(convert(name, rr));
}

if (!sigs && this.zone.zskkey && this.zone.zskpriv) {
// Create dnssec sig on the fly (especially useful for wildcard)
const sig = dnssec.sign(this.zone.zskkey, this.zone.zskpriv, an);
an.push(sig);
}
}

return this;
Expand Down Expand Up @@ -527,19 +618,9 @@ function convert(name, rr) {
if (!isWild(rr.name))
return rr;

const x = util.splitName(name);
const y = util.splitName(rr.name);

assert(y.length > 0);

if (x.length < y.length)
return rr;

rr = rr.clone();

y[0] = x[x.length - y.length];

rr.name = `${y.join('.')}.`;
rr.name = name;

return rr;
}
Expand Down
Loading