dns64

package
v1.8.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 1, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package dns64 implements RFC 6147 DNS64. When an IPv6-only client queries AAAA for a name that has no usable AAAA records but does have A records, DNS64 synthesises AAAA records by embedding the IPv4 addresses inside a configured Pref64::/n IPv6 prefix (RFC 6052). The client receives addresses in a NAT64-routable subnet and can reach the IPv4-only service through a paired NAT64 gateway.

Internal sub-queries skip DNS64 entirely (ClientOnly() == true): the resolver's own NS / glue chase and our secondary A-record lookup must not loop back through us, and the synthesis is a client-facing concern.

Implementation notes:

  • Chain placement is between kubernetes and cache. The cache stores the original AAAA response (NODATA, NXDOMAIN, or AAAA RRset) — synthesis runs per client query against that cached response. The secondary A lookup is itself cached, so repeat synthesis costs an O(few-µs) memcpy plus a cache hit; this preserves per-client correctness when client_networks restricts synthesis.
  • RCODE handling follows RFC 6147 §5.1.2 / §5.1.3 / §5.5. NOERROR with no usable AAAA triggers synthesis. NXDOMAIN passes through unchanged — the name doesn't exist, so it has no A either. SERVFAIL carrying a DNSSEC-failure Extended DNS Error (Unsupported DNSKEY Algorithm / DS Digest Type / NSEC3 Iterations Value, Indeterminate, Bogus, Signature Expired/Not Yet Valid, DNSKEY/RRSIGs/NSEC Missing, No Zone Key Bit Set) also passes through — DNS64 must never paper over a validation failure. Any other nonzero RCODE (plain SERVFAIL, REFUSED, etc.) is treated as "no answer" and attempts synthesis; if the A query is itself empty or errors, that response (rcode + Authority) becomes the basis for the client reply per §5.1.6.
  • AAAA records in the upstream response are filtered against exclude_aaaa_networks (RFC 6147 §5.1.4) before deciding pass-through vs synthesis. The default ::ffff:0:0/96 keeps IPv4-mapped IPv6 from leaking through.
  • Per RFC 6147 §5.5 a NODATA proven by AD=1 from the resolver is downgraded: the synthesised reply clears AD and attaches EDE 4 ("Forged Answer"). A client that set CD=1 is asking to validate itself, so synthesis is skipped.
  • The well-known prefix 64:ff9b::/96 enforces the RFC 6147 §5.1.4 "do not translate" set on the IPv4 side at synthesis time. Operator-chosen prefixes do not.
  • Multiple prefixes synthesise in parallel per RFC 6147 §5.2: every (A, prefix) pair produces a synthesised AAAA so the client gets every reachable Pref64 path.
  • PTR translation per RFC 6147 §5.3.1: ip6.arpa queries whose embedded IPv4 falls inside a configured Pref64 are answered with a CNAME redirect to in-addr.arpa, optionally with the resolved PTR records appended.

Index

Constants

This section is empty.

Variables

View Source
var (
	// Synthesised counts AAAA records produced from A records.
	// Bumped once per client query that resulted in synthesis,
	// not per individual record — the metric tracks how often
	// DNS64 had to step in, not how many addresses were embedded.
	Synthesised = metric.NewCounter(nil, prometheus.CounterOpts{
		Name: "dns64_synthesised_total",
		Help: "Total client AAAA queries answered with synthesised records",
	})

	// Passthrough counts AAAA queries that flowed through DNS64
	// without synthesis, labelled by the reason. "aaaa_present"
	// is the steady-state happy path; the rest are exclusion
	// signals operators may want to monitor.
	Passthrough = metric.NewCounterVec(nil, prometheus.CounterOpts{
		Name: "dns64_passthrough_total",
		Help: "AAAA queries DNS64 left untouched, by reason",
	}, []string{"reason"})

	// ALookupFailures counts secondary A-record lookups that did
	// not yield a usable result (SERVFAIL, no answer, etc.).
	// Reason labels are bounded to a small set so the cardinality
	// stays low.
	ALookupFailures = metric.NewCounterVec(nil, prometheus.CounterOpts{
		Name: "dns64_a_lookup_failures_total",
		Help: "Failures of the secondary A lookup issued during DNS64 synthesis",
	}, []string{"reason"})

	// PTRTranslated counts ip6.arpa PTR queries DNS64 redirected
	// to their in-addr.arpa counterpart per RFC 6147 §5.3.1.
	PTRTranslated = metric.NewCounter(nil, prometheus.CounterOpts{
		Name: "dns64_ptr_translated_total",
		Help: "Total ip6.arpa PTR queries answered with a CNAME to in-addr.arpa",
	})
)

Functions

This section is empty.

Types

type DNS64

type DNS64 struct {
	// contains filtered or unexported fields
}

DNS64 is the middleware handler. The receiver may legitimately be nil — New returns a typed-nil pointer when the middleware is disabled in config. Methods invoked on the typed-nil are no-ops where it makes sense; ServeDNS in particular is never called on a typed-nil because Registry.Build skips disabled handlers.

func New

func New(cfg *config.Config) *DNS64

New constructs a DNS64 handler. Returns a typed-nil pointer when DNS64 is disabled or has no usable configuration; the registry detects that via isNilHandler and skips the middleware entirely.

func (*DNS64) ClientOnly

func (d *DNS64) ClientOnly() bool

ClientOnly excludes DNS64 from the internal sub-pipeline. Internal sub-queries (resolver NS chase, our own secondary A lookup, cache CNAME chase) must not re-enter DNS64 — that would cause infinite synthesis loops and leak synthesised AAAAs into delegation logic.

func (*DNS64) Name

func (d *DNS64) Name() string

Name returns the registered middleware name.

func (*DNS64) ServeDNS

func (d *DNS64) ServeDNS(ctx context.Context, ch *middleware.Chain)

ServeDNS gates synthesis behind cheap fast-path checks before committing to a writer wrap. The wrap (and consequently the secondary A lookup) only happens for client AAAA-class-IN queries that match the configured client networks and aren't on an excluded zone. PTR queries inside ip6.arpa are intercepted here too, when their address falls under one of the configured Pref64 ranges (RFC 6147 §5.3.1).

func (*DNS64) SetQueryer

func (d *DNS64) SetQueryer(q middleware.Queryer)

SetQueryer wires the internal-sub-pipeline Queryer used for the secondary A-record lookup. Auto-wired by middleware.Setup.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL