Safe DNS
Bounded DNS-response parser. Substrate for v0.9.31 b.network.dns.resolver and every consumer that walks raw DNS wire-format bytes (DKIM TXT lookup, MTA-STS verify, DANE TLSA, RBL queries, SVCB / HTTPS discovery, DNSBL via DNS).
Caps every dimension an attacker can grow to DoS the resolver path:
- **Response byte cap** (default 4 KiB; EDNS0 negotiated max 64 KiB — RFC 6891). - **Label count per name** (default 127 — RFC 1035 §2.3.4 absolute cap is 255 octets which bounds to ~127 labels; most legitimate names stay well under 20). - **Compression-pointer chain depth** (default 16 — RFC 1035 allows pointer-to-pointer; unbounded chains cause infinite loops without a depth cap. Common parser-bomb vector). - **CNAME chain depth** (default 8 — matches BIND9's operational cap on canonical-name translations; RFC 1912 §2.4 warns against long CNAME chains; we cap to defend RFC 9156 §3.1 amplification + redirection-loop classes). - **RR count per section** (default 64 answers, 32 authority, 32 additional — total response bounded above by the byte cap, but per-section caps short-circuit malicious sections). - **TXT rdata total length** (default 64 KiB — RFC 1035 §3.3.14 allows up to 65535 octets per RR, but real-world SPF / DKIM / MTA-STS records never approach that; cap defends against amplification).
Throws SafeDnsError on every cap exceeded, malformed name compression, truncated RR, oversize EDNS0 OPT pseudo-RR, RDLENGTH overflow past message end. The parser is purely functional — no I/O, no async — operators run it inline in the resolver path.
Defends the DNS-amplification + parser-bomb classes generally — CVE-2022-3204 (NRDelegationAttack — oversized authority + additional sections backing a malicious non-responsive delegation), CVE-2023-50387 (KeyTrap — DNSKEY+RRSIG combinatorial DoS in validators, mitigated here by per-section RR caps that bound the input to validation), CVE-2023-50868 (NSEC3-encloser companion), CVE-2024-1737 (BIND9 resource exhaustion via large RRsets per hostname). RFC 9156 §3 amplification class.
b.safeDns.parseResponse(buf, opts?) #
{
profile: "strict" | "balanced" | "permissive",
posture: "hipaa" | "pci-dss" | "gdpr" | "soc2",
}
Parse a DNS wire-format response into a structured shape. Returns { id, rcode, flags, question, answer, authority, additional, edns0 }. Each RR carries { name, type, typeName, class, ttl, rdata, decoded } — rdata is the rdlength-bounded byte slice, decoded is the type-specific parse where the parser knows the type (A / AAAA / CNAME / NS / PTR / MX / TXT / SOA / SRV / DS / DNSKEY / TLSA / RRSIG / NSEC / NSEC3 / SVCB / HTTPS), otherwise null.
Throws SafeDnsError with codes: safe-dns/bad-input / oversize-response / truncated-header / truncated-rr / truncated-name / oversize-label / oversize-name / oversize-pointer-depth / oversize-labels / oversize-answer-rrs / oversize-authority-rrs / oversize-additional-rrs / oversize-txt-rdata / oversize-edns0 / malformed-rdlength / bad-profile.
var parsed = b.safeDns.parseResponse(wireBytes);
parsed.answer.forEach(function (rr) {
if (rr.typeName === "TXT") console.log(rr.decoded.join(""));
});
b.safeDns.boundEdns0(advertised, opts?) #
{
profile: "strict" | "balanced" | "permissive",
posture: "hipaa" | "pci-dss" | "gdpr" | "soc2",
}
Clamp an operator-supplied EDNS0 advertised buffer size to the profile cap. Resolver code calls this when constructing a query's OPT pseudo-RR so a misconfigured operator can't advertise a buffer larger than the profile permits.
var udpMax = b.safeDns.boundEdns0(operatorConfig.ednsBuffer);
b.safeDns.checkCnameChainDepth(depth, opts?) #
{
profile: "strict" | "balanced" | "permissive",
posture: "hipaa" | "pci-dss" | "gdpr" | "soc2",
}
Throw if a CNAME-following loop has exceeded the profile's chain depth cap. Called by the resolver as it walks CNAME redirections across follow-up queries (each new query bumps the counter).
for (var i = 0; i < 100; i += 1) {
b.safeDns.checkCnameChainDepth(i);
// ...follow the CNAME if there is one, else break...
break;
}
b.safeDns.compliancePosture(name) #
Return the effective profile NAME for a compliance posture, or null for a name this parser does not map. Unlike the content-guard variant this returns the resolved profile string (every line-protocol parser composes gateContract.ALL_STRICT_POSTURES, so "hipaa" / "pci-dss" / "gdpr" / "soc2" all resolve to "strict") and never throws — the parser shape carries no overlay-clone, no buildProfile, and no loadRulePack. Wired by gateContract.defineParser.
b.safeDns.compliancePosture("hipaa"); // → "strict"
b.safeDns.compliancePosture("not-a-regime"); // → null
Last updated 2026-08-08T16:39:49.652Z by seeder.