diff --git a/lib/resolver/recursive.js b/lib/resolver/recursive.js index cd18b6d..4f870b7 100644 --- a/lib/resolver/recursive.js +++ b/lib/resolver/recursive.js @@ -123,6 +123,11 @@ class RecursiveResolver extends DNSResolver { return this.hints.getAuthority(this.inet6); } + /** + * @param {wire.Question} qs + * @param {Authority} auth + * @returns {Promise<[wire.Message, boolean]>} + */ async ask(qs, auth) { const cache = this.cache.hit(qs, auth.zone); @@ -354,6 +359,62 @@ class RecursiveResolver extends DNSResolver { } } + /** + * A single nameserver may host multiple zones. + * When an RRSIG does not match the current zone, + * we switch zones first before handling trust. + * @param {ResolveContext} rc + */ + async handleDifferentRRSIGZone(rc) { + if (!rc.res.isAnswer()) + return; + + const rrsigs = extractSet(rc.res.answer, '', types.RRSIG); + if (rrsigs.length === 0) + return; + + const signerNames = new Set(rrsigs.map(rrsig => rrsig.data.signerName)); + if (signerNames.has(rc.auth.zone)) + return; + + // There is a zone cut, + // this is the zone we switch to + const [signerName] = signerNames; + + // Get the NS and DS for new zone + const [nsRes] = await this.ask(new Question(signerName, types.NS), rc.auth); + if (!nsRes.isAnswer()) { + this.log('Trust chain broken due to lack of NS record.'); + return; + } + const [dsRes] = await this.ask(new Question(signerName, types.DS), rc.auth); + if (!dsRes.isAnswer()) { + this.log('Trust chain broken due to lack of DS record.'); + return; + } + + // Switch authority/zone + const auth = await this.pickAuthority(rc, nsRes.answer, nsRes.additional); + this.insert(rc); + + if (!auth) + return; + + this.log('Switching authority: %s', auth.name); + this.log('Switching zone: [%s->%s]', rc.auth.zone, auth.zone); + + // Grab DS records for the _next_ zone. + rc.ds = extractSet(dsRes.answer, auth.zone, types.DS); + + if (rc.ds.length === 0) { + rc.chain = false; + this.log('Trust chain broken due to zone change.'); + } + + rc.switchZone(auth); + rc.hop(); + } + async handleTrust(rc) { assert(rc.chain); @@ -485,8 +546,10 @@ class RecursiveResolver extends DNSResolver { async next(rc) { await this.lookupNext(rc); - if (rc.chain) + if (rc.chain) { + await this.handleDifferentRRSIGZone(rc); await this.handleTrust(rc); + } if (rc.chain && rc.res.code === codes.NXDOMAIN) { const nsec = extractSet(rc.res.authority, '', types.NSEC3);