Skip to content

Commit

Permalink
dns: send EDE message with REFUSED
Browse files Browse the repository at this point in the history
  • Loading branch information
pinheadmz committed Sep 14, 2022
1 parent cf2b26f commit 267e441
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 9 deletions.
53 changes: 50 additions & 3 deletions lib/dns/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ class RootServer extends DNSServer {
throw new Error('Tree not available.');

if (!this.chain.safeEntry)
throw new Error('Chain is not safe for name resolution.');
throw new NotReadyError('Chain is not safe for name resolution.');

const {treeRoot} = this.chain.safeEntry;
const hash = rules.hashName(name);
Expand Down Expand Up @@ -389,7 +389,7 @@ class RootServer extends DNSServer {
// (These requests are most
// likely bad anyways)
if (!rules.verifyName(tld))
throw new Error('Invalid name.');
throw new BlockedError('Invalid name.');

// Ask the urkel tree for the name data.
const data = !this.blacklist.has(tld)
Expand Down Expand Up @@ -544,7 +544,13 @@ class RootServer extends DNSServer {
} catch (e) {
this.logger.error(e);
res = new Message();
res.code = codes.REFUSED;

if (e.type === 'EDError') {
res.code = codes.REFUSED;
res.edns.setEDE(e.infoCode, e.message);
} else {
res.code = codes.SERVFAIL;
}
}

return res;
Expand Down Expand Up @@ -807,10 +813,51 @@ class RecursiveServer extends DNSServer {
}
}

/**
* Extended DNS Error
* see https://www.ietf.org/rfc/rfc8914.html
* @extends Error
* @property {Number} infoCode
* @property {String} extraText
*/

class EDError extends Error {
/**
* Create an error.
* @constructor
* @param {Number} infoCode
* @param {String} extraText
*/

constructor(infoCode, msg) {
super();
this.type = 'EDError';
this.infoCode = infoCode;
this.message = msg;

if (Error.captureStackTrace)
Error.captureStackTrace(this, EDError);
}
}

/*
* Helpers
*/

class NotReadyError extends EDError {
constructor(msg) {
// https://www.rfc-editor.org/rfc/rfc8914#name-extended-dns-error-code-14-
super(14, msg);
}
}

class BlockedError extends EDError {
constructor(msg) {
// https://www.rfc-editor.org/rfc/rfc8914#name-extended-dns-error-code-15-
super(15, msg);
}
}

function toKey(name, type) {
const labels = util.split(name);
const label = util.from(name, labels, -1);
Expand Down
61 changes: 55 additions & 6 deletions test/ns-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,19 @@ describe('RootServer', function() {
await ns.close();
});

it('should send SERVFAIL', async () => {
const name = 'macaroni.';
const type = wire.types.A;
const req = {
question: [{name, type}]
};

// Looking up real name without Urkel Tree
assert(!ns.lookup);
const res = await ns.resolve(req);
assert(res.code === wire.codes.SERVFAIL);
});

it('should resolve . NS as SYNTH4', async () => {
// Default
assert.strictEqual(ns.publicHost, '127.0.0.1');
Expand Down Expand Up @@ -329,12 +342,7 @@ describe('RootServer Plugins', function() {

describe('RootServer DNSSEC', function () {
const ns = new RootServer({
chain: {
safeEntry: {
treeRoot: Buffer.alloc(32),
time: Date.now() / 1000
}
},
chain: {},
port: 25349, // regtest
lookup: (hash) => {
assert(hash instanceof Buffer);
Expand Down Expand Up @@ -410,10 +418,51 @@ describe('RootServer DNSSEC', function () {
}
});

it('should refuse to resolve without safe entry', async () => {
const qname = 'pizza.';
const res1 = await resolve(qname, wire.types.NS);
assert.strictEqual(res1.code, wire.codes.REFUSED);

assert(res1.edns);
assert.strictEqual(res1.edns.options.length, 1);
assert.deepStrictEqual(
res1.edns.options[0].getJSON(),
{
code: 'EDE',
option: {
infoCode: 14, // "Not Ready"
extraText: 'Chain is not safe for name resolution.'
}
}
);

// Confirm imaginary blocks
ns.chain.safeEntry = {
treeRoot: Buffer.alloc(32),
time: Date.now() / 1000
};

const res2 = await resolve(qname, wire.types.NS);
assert.strictEqual(res2.code, wire.codes.NXDOMAIN);
});

it('should refuse invalid names', async () => {
const qname = 'example\\000';
const res = await resolve(qname, wire.types.NS);
assert.strictEqual(res.code, wire.codes.REFUSED);

assert(res.edns);
assert.strictEqual(res.edns.options.length, 1);
assert.deepStrictEqual(
res.edns.options[0].getJSON(),
{
code: 'EDE',
option: {
infoCode: 15, // "Blocked"
extraText: 'Invalid name.'
}
}
);
});

it('should prove NXDOMAIN', async () => {
Expand Down

0 comments on commit 267e441

Please sign in to comment.