Skip to content

Commit

Permalink
Fix 2625 - Add support for EDNS(0)
Browse files Browse the repository at this point in the history
  • Loading branch information
Geod24 committed Nov 11, 2021
1 parent 0bef79b commit 3eb29b1
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 0 deletions.
33 changes: 33 additions & 0 deletions source/agora/common/DNS.d
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import agora.serialization.Serializer;
import std.algorithm.iteration;
import std.bitmanip;
import std.format;
import std.range;
import std.string;
static import std.utf;

Expand Down Expand Up @@ -310,6 +311,38 @@ public struct Message
this.authorities.each!(e => serializePart(e, dg, CompactMode.No));
this.additionals.each!(e => serializePart(e, dg, CompactMode.No));
}

/***************************************************************************
Gives the estimated serialized size
This routine can be used to avoid serialization if it goes over the
payload size (either 512 or EDNS0-set) expectation.
Note that this does not take into account compression, which could
significantly reduce the size.
***************************************************************************/

public size_t maxSerializedSize () const scope @safe pure nothrow @nogc
{
// Header is a POD without indirection
size_t size = Header.sizeof;

// Question is QTYPE + QCLASS + Domain
// Domain's serialized size is at most `data.length + 1`,
// and at least 2 bytes (a pointer)
foreach (const ref q; this.questions)
size += (QTYPE.sizeof + QCLASS.sizeof + q.qname.length + 1);

auto allRRs = this.answers.chain(this.authorities).chain(this.additionals);
foreach (const ref a; allRRs)
{
size += (a.name.length + 1 + TYPE.sizeof + CLASS.sizeof +
uint.sizeof + ushort.sizeof + a.rdata.length);
}

return size;
}
}

/// https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1
Expand Down
46 changes: 46 additions & 0 deletions source/agora/node/Registry.d
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,42 @@ public class NameRegistry: NameRegistryAPI
reply.header.RA = false; // TODO: Implement
reply.header.AA = true; // TODO: Make configurable

// EDNS(0) support
// payloadSize must be treated to be at least 512. A payloadSize of 0
// means no OPT record were found (there should be only one),
// the requestor does not support EDNS0, and we should not include
// an OPT record in our answer.
ushort payloadSize;
foreach (const ref add; query.additionals)
{
if (add.type == TYPE.OPT)
{
// This is a second OPT record, which is illegal by spec and
// triggers a FORMERR
if (payloadSize > 0)
goto BAILOUT;

scope opt = const(OPTRR)(add);
// 6.1.1: If an OPT record is present in a received request,
// compliant responders MUST include an OPT record in their
// respective responses.
OPTRR responseOPT;

if (opt.EDNSVersion() > 0)
{
responseOPT.extendedRCODE = 1; // BADVERS
reply.additionals ~= responseOPT.record;
goto BAILOUT;
}
// Ignore the DO bit for now
payloadSize = min(opt.payloadSize(), ushort(512));
reply.additionals ~= responseOPT.record;
}
}
// No OPT record present, the client does not support EDNS
if (payloadSize == 0)
payloadSize = 512;

// Note: Since DNS has some fields which apply to the full response but
// should actually be in `answers`, most resolvers will not ask unrelated
// questions / will only ask one question at a time.
Expand Down Expand Up @@ -363,7 +399,17 @@ public class NameRegistry: NameRegistryAPI
log.warn("Refusing {} for unknown zone: {}", q.qtype, q.qname);
break;
}

if (reply.maxSerializedSize() > payloadSize)
{
reply.questions = reply.questions[0 .. $ - 1];
reply.answers = reply.answers[0 .. $ - 1];
reply.header.TC = true;
break;
}
}

BAILOUT:
reply.fill(query.header);
log.trace("{} DNS query: {} => {}",
(reply.header.RCODE == Header.RCode.NoError) ? "Fullfilled" : "Unsuccessfull",
Expand Down

0 comments on commit 3eb29b1

Please sign in to comment.