cache

package
v1.8.1 Latest Latest
Warning

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

Go to latest
Published: Aug 24, 2026 License: MIT Imports: 33 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// DefaultFailureCacheSize bounds retained exact and zone failure states.
	DefaultFailureCacheSize = 4096
	// DefaultFailureInitialTTL is the first cached-failure backoff interval.
	DefaultFailureInitialTTL = 5 * time.Second
	// DefaultFailureMaxTTL caps cached-failure backoff.
	DefaultFailureMaxTTL = 5 * time.Minute
)

Variables

This section is empty.

Functions

func AcquireMsg added in v1.0.0

func AcquireMsg() *dns.Msg

AcquireMsg returns an empty msg from pool with pre-allocated slices.

func ReleaseMsg added in v1.0.0

func ReleaseMsg(m *dns.Msg)

ReleaseMsg returns msg to pool.

func SetCacheSizeFuncs added in v1.6.1

func SetCacheSizeFuncs(positive, negative func() int, extras ...func() int)

SetCacheSizeFuncs sets the functions to get cache sizes. Optional functions preserve the original two-argument source contract: extras[0] is the RFC 9520 failure cache, extras[1] the RFC 8020 cut index, and extras[2] the RFC 8198 proof index.

func SetMetricsInstance added in v1.6.1

func SetMetricsInstance(m *CacheMetrics)

SetMetricsInstance sets the metrics instance for hit rate calculation

func UpdateCacheSizeMetrics added in v1.6.1

func UpdateCacheSizeMetrics()

UpdateCacheSizeMetrics updates the cache size gauges

Types

type Cache

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

Cache is the cache implementation.

func New

func New(cfg *config.Config) *Cache

New creates a new cache.

func (*Cache) ForEachEntry added in v1.6.2

func (c *Cache) ForEachEntry(fn func(positive bool, key uint64, entry *CacheEntry) bool)

ForEachEntry iterates over positive and negative cache entries. Iteration is not atomic with concurrent updates. Returning false from fn stops iteration.

func (*Cache) InlineBarrier added in v1.8.0

func (c *Cache) InlineBarrier() bool

InlineBarrier declares that the cache honors Chain.InlineOnly: an inline pass gets the wire ladder and nothing below it — everything past the ladder can materialize, wait, or resolve, and a transport reader can afford none of those. Its presence is what lets the server turn the reader fast path on at all.

func (*Cache) Name

func (c *Cache) Name() string

(*Cache).Name name returns middleware name.

func (*Cache) Purge added in v1.6.4

func (c *Cache) Purge(q dns.Question)

(*Cache).Purge removes positive and negative cache entries for q under both CD=true and CD=false. Implements middleware.Purger so the api purge endpoint can invalidate cache state without synthesising a CHAOS-NULL request.

func (*Cache) ServeDNS

func (c *Cache) ServeDNS(ctx context.Context, ch *middleware.Chain)

(*Cache).ServeDNS serveDNS implements the middleware.Handler interface. A wire-born request first tries the wire fast path — the exact-entry lookup on the wire question through the canonical key, served through the writer lease without a decoded request. Everything else — misses, composite candidates (NXDOMAIN cut, aggressive denial, failure cache), scoped/ECS traffic, prefetch-due entries, Msg-path-only hit shapes — falls through to materialization and the ordinary body with every gate and lookup it has today.

func (*Cache) Set

func (c *Cache) Set(key uint64, msg *dns.Msg)

(*Cache).Set adds a new element to the cache. It is retained for source compatibility with prefetch and plugin callers. Ordinary answers honor the supplied answer-cache key. SERVFAIL uses its full DNS Question instead: shared RFC 9520 state verifies the complete key preimage and cannot safely accept an opaque hash as its identity.

Internally the entry is constructed with origTTL adjusted to the real upstream TTL so subsequent prefetch decisions don't miscalculate from the post-Calculate (clamped) value.

func (*Cache) SetDNSSECCryptoLimiter added in v1.7.4

func (c *Cache) SetDNSSECCryptoLimiter(limiter middleware.DNSSECCryptoLimiter)

SetDNSSECCryptoLimiter installs the resolver-owned gate used by optional NSEC3 proof lookups. Called once by middleware.Setup before publication.

func (*Cache) SetPrefetchQueryer added in v1.6.4

func (c *Cache) SetPrefetchQueryer(q middleware.Queryer)

(*Cache).SetPrefetchQueryer installs the Queryer used by the prefetch worker. The prefetch sub-pipeline excludes the cache middleware so a refresh reaches the upstream resolver / forwarder instead of returning its own about-to-expire entry.

func (*Cache) SetQueryer added in v1.6.4

func (c *Cache) SetQueryer(q middleware.Queryer)

(*Cache).SetQueryer installs the Queryer used for internal client-shaped work (CNAME chase on cache writeback, future DNAME target lookup from the resolver). Called once from sdns.go startup.

func (*Cache) Stats added in v1.5.0

func (c *Cache) Stats() map[string]any

(*Cache).Stats stats returns cache statistics.

func (*Cache) Stop added in v1.5.0

func (c *Cache) Stop()

(*Cache).Stop stop gracefully shuts down the cache.

func (*Cache) Store added in v1.6.4

func (c *Cache) Store() middleware.Store

(*Cache).Store returns the storage facade, typed as middleware.Store so Cache satisfies middleware.StoreProvider for auto-wiring in middleware.Setup. External callers that need the full *Store surface can type-assert.

type CacheConfig added in v1.5.0

type CacheConfig struct {
	Size        int
	Prefetch    int
	PositiveTTL time.Duration
	NegativeTTL time.Duration
	MinTTL      time.Duration
	MaxTTL      time.Duration
	RateLimit   int

	// ECSMaxTTL caps the lifetime of cache entries keyed under an
	// ECS scope. Geo-routed answers tend to go stale faster than
	// the resolver's general MaxTTL would suggest — a CDN
	// re-pointing a /24 between PoPs is normal traffic. Zero
	// disables the cap (scoped entries live as long as their
	// upstream TTL allowed). Populated from cfg.ECS.CacheLimitTTL.
	ECSMaxTTL time.Duration
}

CacheConfig holds cache configuration with validation.

func (CacheConfig) Validate added in v1.5.0

func (cc CacheConfig) Validate() error

(CacheConfig).Validate validate checks if the configuration is valid.

type CacheEntry added in v1.5.0

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

CacheEntry represents an immutable cache entry.

The response is retained in packed wire form: one pointer-free byte slice instead of a parsed message graph of ~two dozen heap objects. At the field scale that graph was the dominant GC mark cost (18M live objects on a warm 743k-entry canary). Serving unpacks a fresh message per hit — comparable work to the deep Copy the parsed representation already paid, since a served message must be privately mutable either way.

func NewCacheEntry added in v1.5.0

func NewCacheEntry(msg *dns.Msg, ttl time.Duration, rateLimit int) *CacheEntry

NewCacheEntry creates a new cache entry from a DNS message.

func NewCacheEntryWithKey added in v1.6.0

func NewCacheEntryWithKey(msg *dns.Msg, ttl time.Duration, rateLimit int, key uint64) *CacheEntry

NewCacheEntryWithKey creates a new cache entry with a specific key for rate limiting. A message that cannot be packed returns nil — such a response was never servable on the wire, so declining to cache it is the safe outcome.

func NewScopedCacheEntry added in v1.7.0

func NewScopedCacheEntry(msg *dns.Msg, ttl time.Duration, rateLimit int, scope netip.Prefix) *CacheEntry

NewScopedCacheEntry creates a cache entry that's been keyed under an ECS scope. scope MUST be the prefix the entry's key was computed from — the hit-path verifier compares the two exactly, so an entry admitted under a scope it does not carry is unreachable. A /0 or invalid scope leaves the entry shared, matching CacheKey.Hash, which collapses /0 to the unscoped key.

func (*CacheEntry) GetRateLimiter added in v1.6.0

func (e *CacheEntry) GetRateLimiter() *rate.Limiter

(*CacheEntry).GetRateLimiter returns the shared rate limiter for this entry

func (*CacheEntry) IsExpired added in v1.5.0

func (e *CacheEntry) IsExpired() bool

(*CacheEntry).IsExpired isExpired checks if the cache entry has expired.

func (*CacheEntry) PrefetchEligible added in v1.7.0

func (e *CacheEntry) PrefetchEligible() bool

PrefetchEligible reports whether the prefetch worker may refresh this entry. Scoped entries are skipped because the worker has no client IP, so a refresh would lose the ECS scope and create a shared-key entry instead — wrong answer for the wrong audience.

func (*CacheEntry) ShouldPrefetch added in v1.5.0

func (e *CacheEntry) ShouldPrefetch(threshold int) bool

(*CacheEntry).ShouldPrefetch shouldPrefetch checks if this entry should be prefetched.

func (*CacheEntry) TTL added in v1.5.0

func (e *CacheEntry) TTL() int

(*CacheEntry).TTL TTL returns the remaining TTL in seconds.

func (*CacheEntry) ToMsg added in v1.5.0

func (e *CacheEntry) ToMsg(req *dns.Msg) *dns.Msg

(*CacheEntry).ToMsg toMsg creates a response message with updated TTLs.

type CacheKey added in v1.5.0

type CacheKey struct {
	Question dns.Question
	CD       bool
	Scope    netip.Prefix
}

CacheKey represents a structured cache key.

Scope is the ECS prefix (RFC 7871) the authority claimed its answer is scoped to. The zero value means "shared" — an entry keyed with no scope is reachable by any client, which is the pre-Stage-2 default and how non-ECS traffic and SCOPE=0 authority answers continue to behave after the upgrade.

func (CacheKey) Hash added in v1.5.0

func (k CacheKey) Hash() uint64

(CacheKey).Hash returns the cache key hash. Routes to cache.KeyWithPrefix when Scope is valid (family + bit-length + address are all folded in so /22 and /24 of the same address don't alias), and to the legacy cache.Key — bit-identical to pre-Stage-2 — when Scope is the zero value, so old entries keep hitting after upgrade. A /0 scope collapses to the unscoped key because a /0 answer is semantically "global", same as no scope.

type CacheMetrics added in v1.5.0

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

CacheMetrics tracks cache performance metrics.

func (*CacheMetrics) Eviction added in v1.5.0

func (m *CacheMetrics) Eviction()

(*CacheMetrics).Eviction eviction records a cache eviction.

func (*CacheMetrics) Hit added in v1.5.0

func (m *CacheMetrics) Hit()

(*CacheMetrics).Hit hit records a cache hit.

func (*CacheMetrics) Miss added in v1.5.0

func (m *CacheMetrics) Miss()

(*CacheMetrics).Miss miss records a cache miss.

func (*CacheMetrics) Prefetch added in v1.5.0

func (m *CacheMetrics) Prefetch()

(*CacheMetrics).Prefetch prefetch records a prefetch operation.

func (*CacheMetrics) Stats added in v1.5.0

func (m *CacheMetrics) Stats() (hits, misses, evictions, prefetches int64)

(*CacheMetrics).Stats stats returns current metrics.

type DCache added in v1.6.4

type DCache interface {
	Get(key uint64) (*CacheEntry, bool)
	Set(key uint64, entry *CacheEntry)
	Remove(key uint64)
	Len() int
}

DCache defines the interface for DNS cache implementations.

type FailureCache added in v1.7.4

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

FailureCache is a bounded, concurrent cache of recursive-resolution failures. Expired entries remain as backoff history but are not cache hits.

func NewFailureCache added in v1.7.4

func NewFailureCache(cfg FailureCacheConfig) (*FailureCache, error)

NewFailureCache constructs a bounded failure cache.

func (*FailureCache) Len added in v1.7.4

func (c *FailureCache) Len() int

Len returns the number of retained active and expired states.

func (*FailureCache) Lookup added in v1.7.4

func (c *FailureCache) Lookup(key FailureQuestionKey) (FailureHit, bool)

Lookup returns an active exact failure, or otherwise the closest active ancestor-zone failure. Expired state is retained for Record and RetryKey but never returned as a hit.

func (*FailureCache) LookupWire added in v1.8.0

func (c *FailureCache) LookupWire(name []byte, qtype, qclass uint16, cd bool) (FailureHit, bool)

LookupWire is Lookup for a wire-born question without an ECS scope: the same exact-question probe and ancestor-zone walk, keyed through the canonical wire hashes (bit-identical to the presentation ones) and verified by fold comparison — no strings, no allocation. Scoped entries never match: the wire path carries no client scope, and scope is part of both the hash and the verification.

func (*FailureCache) PurgeQuestion added in v1.7.4

func (c *FailureCache) PurgeQuestion(q dns.Question) int

PurgeQuestion removes every CD/ECS variant of an exact question and a zone-wide state whose owner is the purged name. Purge is an explicit, infrequent operator action, so a bounded O(n) scan avoids maintaining a second attacker-influenced index.

func (*FailureCache) RecordQuestion added in v1.7.4

func (c *FailureCache) RecordQuestion(key FailureQuestionKey, provenance FailureProvenance, witness []denialWitnessPair) FailureHit

RecordQuestion records an exact failure. Repeated calls during an active generation are idempotent. After expiry, concurrent recorders advance the streak exactly once.

func (*FailureCache) RecordZone added in v1.7.4

func (c *FailureCache) RecordZone(key FailureZoneKey, provenance FailureProvenance, witness []denialWitnessPair) FailureHit

RecordZone records a zone-wide reachability failure. Repeated calls during an active generation are idempotent. Zone failures are not partitioned by CD or ECS because authority transport reachability is shared.

func (*FailureCache) ResetMatching added in v1.7.4

func (c *FailureCache) ResetMatching(key FailureQuestionKey) int

ResetMatching removes exact history and every ancestor-zone history covered by a fresh useful response. Recovery deliberately deletes the streak: RFC 9520 backoff applies to persistent failures, so a later independent failure starts a new episode at the initial interval. Retaining an expired streak here would also keep healthy descendants in the retry-generation path. It returns the number of states deleted.

func (*FailureCache) ResetQuestion added in v1.7.4

func (c *FailureCache) ResetQuestion(key FailureQuestionKey) bool

ResetQuestion deletes exact history when the full key still matches.

func (*FailureCache) ResetZone added in v1.7.4

func (c *FailureCache) ResetZone(key FailureZoneKey) bool

ResetZone deletes zone history when the full key still matches.

func (*FailureCache) RetryKey added in v1.7.4

func (c *FailureCache) RetryKey(key FailureQuestionKey) (uint64, bool)

RetryKey returns a stable singleflight key for a matching expired failure generation. An active exact or ancestor-zone state returns false because it should be consumed through Lookup instead. Closest-zone history takes precedence over exact history so different random QNAMEs below a failed authority converge on one probe generation.

func (*FailureCache) Stop added in v1.7.4

func (c *FailureCache) Stop()

Stop releases resources owned by the backing cache.

type FailureCacheConfig added in v1.7.4

type FailureCacheConfig struct {
	Size       int
	InitialTTL time.Duration
	MaxTTL     time.Duration
	Now        func() time.Time
}

FailureCacheConfig configures the bounded failure cache. Zero TTL values use DefaultFailureInitialTTL and DefaultFailureMaxTTL. Now is injectable for deterministic tests; nil uses time.Now.

type FailureHit added in v1.7.4

type FailureHit struct {
	Kind       FailureKind
	Provenance FailureProvenance
	Streak     uint32
	RetryAfter time.Time
	Question   FailureQuestionKey
	Zone       FailureZoneKey
	// contains filtered or unexported fields
}

FailureHit is an immutable snapshot of an active cached failure.

func (FailureHit) Response added in v1.7.4

func (h FailureHit) Response(req *dns.Msg) *dns.Msg

Response builds a clean SERVFAIL response for a cached failure. EDE 13 is emitted only when the client sent EDNS, and no client EDNS options are copied.

type FailureKind added in v1.7.4

type FailureKind uint8

FailureKind identifies whether a hit is question-specific or zone-wide.

const (
	// FailureKindQuestion is an exact QNAME/QTYPE/QCLASS+CD+ECS failure.
	FailureKindQuestion FailureKind = iota + 1
	// FailureKindZone is an authority-zone reachability failure.
	FailureKindZone
)

type FailureProvenance added in v1.7.4

type FailureProvenance string

FailureProvenance identifies the failure class that admitted an entry. Admission policy belongs to the caller; the cache only retains the value for observability and for consumers that need to distinguish failure sources.

type FailureQuestionKey added in v1.7.4

type FailureQuestionKey struct {
	Question dns.Question
	CD       bool
	Scope    netip.Prefix
}

FailureQuestionKey isolates failures by the complete DNS question, CD bit, and ECS audience. Invalid and /0 scopes are the shared global audience.

type FailureZoneKey added in v1.7.4

type FailureZoneKey struct {
	Zone   string
	Qclass uint16
}

FailureZoneKey identifies a zone-wide reachability failure. Transport reachability is independent of the client's CD bit and ECS audience.

type NegativeCache added in v1.5.0

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

NegativeCache handles error DNS responses.

func NewNegativeCache added in v1.5.0

func NewNegativeCache(size int, minTTL, maxTTL time.Duration, metrics *CacheMetrics) *NegativeCache

NewNegativeCache creates a new negative cache.

func (*NegativeCache) Get added in v1.5.0

func (nc *NegativeCache) Get(key uint64) (*CacheEntry, bool)

(*NegativeCache).Get get retrieves an entry from the negative cache. Hit/Miss metrics are NOT recorded here — see PositiveCache.Get.

func (*NegativeCache) Len added in v1.5.0

func (nc *NegativeCache) Len() int

(*NegativeCache).Len len returns the number of entries in the cache.

func (*NegativeCache) Remove added in v1.5.0

func (nc *NegativeCache) Remove(key uint64)

(*NegativeCache).Remove remove deletes an entry from the negative cache.

func (*NegativeCache) Set added in v1.5.0

func (nc *NegativeCache) Set(key uint64, entry *CacheEntry)

(*NegativeCache).Set set stores an entry in the negative cache.

type PositiveCache added in v1.5.0

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

PositiveCache handles successful DNS responses.

func NewPositiveCache added in v1.5.0

func NewPositiveCache(size int, minTTL, maxTTL time.Duration, metrics *CacheMetrics) *PositiveCache

NewPositiveCache creates a new positive cache.

func (*PositiveCache) Get added in v1.5.0

func (pc *PositiveCache) Get(key uint64) (*CacheEntry, bool)

(*PositiveCache).Get get retrieves an entry from the positive cache. Hit/Miss metrics are NOT recorded here — checkCache consults both positive and negative caches per request and records the aggregate result once, so pushing metrics in here would double-count both sides of a single miss.

func (*PositiveCache) Len added in v1.5.0

func (pc *PositiveCache) Len() int

(*PositiveCache).Len len returns the number of entries in the cache.

func (*PositiveCache) Remove added in v1.5.0

func (pc *PositiveCache) Remove(key uint64)

(*PositiveCache).Remove remove deletes an entry from the positive cache.

func (*PositiveCache) Set added in v1.5.0

func (pc *PositiveCache) Set(key uint64, entry *CacheEntry)

(*PositiveCache).Set set stores an entry in the positive cache.

type PrefetchQueue added in v1.5.0

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

PrefetchQueue manages prefetch requests with worker pool.

func NewPrefetchQueue added in v1.5.0

func NewPrefetchQueue(workers, queueSize int, metrics *CacheMetrics) *PrefetchQueue

NewPrefetchQueue creates a new prefetch queue.

func (*PrefetchQueue) Add added in v1.5.0

func (pq *PrefetchQueue) Add(req PrefetchRequest) bool

(*PrefetchQueue).Add add queues a prefetch request.

func (*PrefetchQueue) Stop added in v1.5.0

func (pq *PrefetchQueue) Stop()

(*PrefetchQueue).Stop stop gracefully shuts down the prefetch queue.

type PrefetchRequest added in v1.5.0

type PrefetchRequest struct {
	Request       *dns.Msg
	Key           uint64
	Cache         *Cache      // Reference to the cache to store prefetched results
	Entry         *CacheEntry // Entry that claimed the prefetch; used to release the claim on failure/drop
	RequestHadECS bool        // Original client ECS, retained after EDNS policy stripping
}

PrefetchRequest represents a DNS query to be prefetched.

type ResponseWriter

type ResponseWriter struct {
	middleware.ResponseWriter
	// contains filtered or unexported fields
}

ResponseWriter is the response writer for cache.

func (*ResponseWriter) WriteMsg

func (w *ResponseWriter) WriteMsg(res *dns.Msg) error

(*ResponseWriter).WriteMsg writeMsg implements the ResponseWriter interface.

type Store added in v1.6.4

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

Store is the cache backing for the cache middleware. The answer and failure sub-caches are constructed by Cache.New and shared with Store; Store owns the separately bounded RFC 8020 cut index. It centralises classification, keying, and TTL handling so callers outside ServeDNS (resolver sub-queries, queryer-driven prefetch, future API purge wiring) don't need to understand the wire-write rules in ResponseWriter.WriteMsg.

SetFromResponse keys on the caller-supplied keyCD rather than on resp.CheckingDisabled to make the keying contract explicit at every call site — Forwarder and Resolver both temporarily mutate CD on the upstream request, and silent reliance on resp.CheckingDisabled is the kind of thing that splits CD=1 and CD=0 lookups across stale entries when a future change forgets to restore.

func NewStore added in v1.6.4

func NewStore(positive *PositiveCache, negative *NegativeCache, cfg CacheConfig, failures ...*FailureCache) *Store

NewStore returns a Store backed by the supplied sub-caches. The caches are shared with the surrounding *Cache; this constructor does not allocate them.

func (*Store) ClearZoneFailure added in v1.7.4

func (s *Store) ClearZoneFailure(q dns.Question, zone string)

ClearZoneFailure removes authority reachability history after the resolver receives a useful response from that same zone. Local/static cache writers never call this, so a hosts-file answer cannot falsely mark an upstream zone as recovered.

func (*Store) DenialMissHoldsWire added in v1.8.0

func (s *Store) DenialMissHoldsWire(name []byte, qclass uint16, hit FailureHit) bool

DenialMissHoldsWire reports whether a failure entry's record-time proof that aggressive denial does not apply is still valid for this wire-born name — the condition under which the wire path may serve the failure without materializing.

func (*Store) DenialProofBytes added in v1.7.4

func (s *Store) DenialProofBytes() int64

DenialProofBytes returns the retained DNS wire bytes.

func (*Store) DenialProofLen added in v1.7.4

func (s *Store) DenialProofLen() int

DenialProofLen returns retained SOA and denial RRset entries.

func (*Store) DenialProofZones added in v1.7.4

func (s *Store) DenialProofZones() int

DenialProofZones returns the number of signer-zone shards.

func (*Store) FailureLen added in v1.7.4

func (s *Store) FailureLen() int

FailureLen returns retained active and expired RFC 9520 failure states.

func (*Store) FailureRetryKey added in v1.7.4

func (s *Store) FailureRetryKey(req *dns.Msg, scope netip.Prefix) (uint64, bool)

FailureRetryKey returns the retained failure generation used to coalesce the first retry after a backoff expires. Different random QNAMEs below the same failed zone therefore elect only one probe leader.

func (*Store) ForEach added in v1.6.4

func (s *Store) ForEach(fn func(positive bool, key uint64, entry *CacheEntry) bool)

ForEach iterates over positive then negative entries. Returning false from fn stops iteration. Iteration is not atomic with concurrent updates.

func (*Store) Get added in v1.6.4

func (s *Store) Get(req *dns.Msg) (*dns.Msg, bool)

Get returns a materialised cached response for req. Reply header/ID/CD/AD interaction is handled by CacheEntry.ToMsg, which needs req for SetReply.

func (*Store) GetWithContext added in v1.7.4

func (s *Store) GetWithContext(ctx context.Context, req *dns.Msg) (*dns.Msg, bool)

GetWithContext is Get with request-tree policy and NSEC3 memo propagation. Resolver-private DS/DNSKEY lookups manufacture fresh messages, so the context marker is what preserves an outer client's CD/ECS isolation.

func (*Store) Lookup added in v1.6.4

func (s *Store) Lookup(req *dns.Msg) (*CacheEntry, bool)

Lookup returns the cache entry for req without materialising a response. Used by callers that need to inspect TTL or prefetch flags before deciding whether to consume the entry.

func (*Store) LookupByKey added in v1.6.4

func (s *Store) LookupByKey(key uint64) (*CacheEntry, bool)

LookupByKey is the pre-keyed form of Lookup, used by hot paths inside the cache middleware that already computed the key.

Callers MUST verify the returned entry against the preimage they keyed with (use LookupByKeyVerified, or entryMatchesKey / entryMatchesWire): the map key is a non-cryptographic 64-bit xxhash of that preimage, so a hash collision would otherwise serve one query's answer to another.

func (*Store) LookupByKeyVerified added in v1.7.1

func (s *Store) LookupByKeyVerified(key uint64, want CacheKey) (*CacheEntry, bool)

LookupByKeyVerified is LookupByKey plus a full-key check: it confirms the stored entry was admitted under exactly the preimage want describes before returning it. Without this, two distinct preimages that collide under xxhash64 would silently serve each other's answers — and because qnames are attacker-chosen, a collision can be searched for offline and used to poison the cache.

func (*Store) LookupDenialProof added in v1.7.4

func (s *Store) LookupDenialProof(
	req *dns.Msg,
	work dnssec.NSEC3Work,
) (*dns.Msg, middleware.ValidatedNegativeProofKind, string, bool)

LookupDenialProof evaluates the bounded RFC 8198 proof index for req. The returned kind and signer zone describe the exact proof family selected by the evaluator, including when a DO=0 response omits DNSSEC records.

func (*Store) LookupFailure added in v1.7.4

func (s *Store) LookupFailure(req *dns.Msg, scope netip.Prefix) (FailureHit, bool)

LookupFailure returns an active exact or closest-ancestor RFC 9520 failure.

func (*Store) LookupFailureWire added in v1.8.0

func (s *Store) LookupFailureWire(name []byte, qtype, qclass uint16, cd bool) (FailureHit, bool)

LookupFailureWire is LookupFailure for a wire-born question without an ECS scope.

func (*Store) LookupNXDomainCut added in v1.7.4

func (s *Store) LookupNXDomainCut(req *dns.Msg) (*nxDomainCutEntry, bool)

LookupNXDomainCut returns the closest locally validated RFC 8020 cut that covers req. CD=1 is always a miss: checking-disabled clients explicitly bypass locally synthesized authenticated denial state.

func (*Store) LookupNXDomainCutWire added in v1.8.0

func (s *Store) LookupNXDomainCutWire(name []byte, qclass uint16) (*nxDomainCutEntry, bool)

LookupNXDomainCutWire is LookupNXDomainCut for a wire-born question: same store, same walk, no decoded message. CD gating is the caller's — the wire path mirrors the Msg entry's bypass conditions inline.

func (*Store) NXDomainCutLen added in v1.7.4

func (s *Store) NXDomainCutLen() int

NXDomainCutLen returns retained locally validated RFC 8020 cuts.

func (*Store) NegativeLen added in v1.6.4

func (s *Store) NegativeLen() int

NegativeLen returns the number of entries in the negative cache.

func (*Store) PositiveLen added in v1.6.4

func (s *Store) PositiveLen() int

PositiveLen returns the number of entries in the positive cache.

func (*Store) Purge added in v1.6.4

func (s *Store) Purge(q dns.Question)

Purge removes both CD=true and CD=false entries for q from positive and negative caches, including ECS-scoped entries.

Scoped entries don't have a deterministic key the caller could reproduce without enumerating every (qname, scope) the cache has ever seen — there's no per-qname index. We sweep them with ForEach: collect matching keys in one pass (snapshotting the per-segment locks individually), then Remove outside the iteration to avoid mutate-during-iterate hazards.

O(n) on the cache size; Purge is rare (explicit operator API call) so the linear scan is acceptable. If purge becomes hot, a per-qname index would lift this back to O(matches).

func (*Store) RecordDenialProof added in v1.7.4

func (s *Store) RecordDenialProof(
	proof *dns.Msg,
	zone string,
	kind middleware.ValidatedNegativeProofKind,
	cutUntil time.Time,
) bool

RecordDenialProof stores complete signed SOA and NSEC/NSEC3 RRsets from an exact locally validated terminal proof. kind is checked against the retained mechanism so provenance cannot be reinterpreted by a later cache layer.

func (*Store) RecordFailure added in v1.7.4

func (s *Store) RecordFailure(req *dns.Msg, scope netip.Prefix, provenance FailureProvenance, witness []denialWitnessPair)

RecordFailure records a question-specific terminal resolution failure. witness must be the miss witness captured at the moment the recording request's denial rung ran and missed (Cache.ServeDNS stashes it on the response writer), and nil from every path that never ran the rung — a CD or client-ECS tree that bypassed it, a compatibility Set, a scoped insert. A witness fabricated later would assert a miss nobody established at a moment nobody checked.

func (*Store) RecordNXDomainCut added in v1.7.4

func (s *Store) RecordNXDomainCut(
	proof *dns.Msg,
	deniedName string,
	zone string,
	cutUntil time.Time,
) bool

RecordNXDomainCut stores a locally validated terminal NXDOMAIN proof. deniedName is the exact authoritative query cycle that returned NXDOMAIN; it must never be inferred from the SOA owner.

func (*Store) RecordZoneFailure added in v1.7.4

func (s *Store) RecordZoneFailure(q dns.Question, zone string)

RecordZoneFailure implements middleware.ResolutionFailureStore. Zone-wide reachability failures are deliberately independent of CD and ECS.

func (*Store) ReplaceIfCurrent added in v1.7.3

func (s *Store) ReplaceIfCurrent(key uint64, expected *CacheEntry, resp *dns.Msg, cutUntil time.Time, cutKey uint64) bool

ReplaceIfCurrent stores resp under key only if expected is still the live entry for that key — the pointer-CAS late-write guard for asynchronous refreshes (GHSA-mqfw-f48p-2vc8). A prefetch captures the entry that claimed it; by the time its refresh returns, newer state (a withdrawal NXDOMAIN, a re-delegated answer) may have replaced that entry, and the stale result must be dropped, not stored. Returns whether the replacement happened.

Two deliberate asymmetries:

  • A SERVFAIL refresh never displaces a positive entry: it only CASes into the negative cache, so a transient upstream failure can't evict a still-valid answer. Production Cache SERVFAILs use FailureCache; this branch preserves the exported Store/NegativeCache contract for programmatic callers and manually seeded legacy entries.
  • The CAS stays within one sub-cache. A negative entry refreshing to a positive answer is dropped rather than promoted — the two caches can't be swapped atomically, and the negative entry's short TTL re-resolves naturally.

func (*Store) SetEntryWithKey added in v1.6.4

func (s *Store) SetEntryWithKey(key uint64, entry *CacheEntry, mt dnsutil.ResponseType)

SetEntryWithKey replaces a stored entry directly. Used by Cache.Set's compatibility path where a caller already constructed the CacheEntry (e.g. prefetch worker writing back a response with adjusted origTTL).

func (*Store) SetFromResponse added in v1.6.4

func (s *Store) SetFromResponse(resp *dns.Msg, keyCD bool, cutUntil time.Time)

SetFromResponse classifies resp (answer / NXDOMAIN+NODATA / resolution failure) and stores it under (resp.Question[0], keyCD). CHAOS signalling responses are skipped, matching ResponseWriter.WriteMsg. cutUntil bounds the entry to the delegation cut that produced it; zero means unbounded. This compatibility entry point has no lineage identity.

func (*Store) SetFromResponseScoped added in v1.7.0

func (s *Store) SetFromResponseScoped(key uint64, resp *dns.Msg, scope netip.Prefix, cutUntil time.Time, cutKey uint64)

SetFromResponseScoped is SetFromResponseWithKey for entries that were keyed under an ECS scope (RFC 7871 §7.1.2). scope must be the prefix key was computed from: the entry carries it, and the hit-path verifier compares the two so a colliding key cannot cross ECS audiences. The entry's PrefetchEligible is false — the prefetch worker has no client IP to derive ECS from, so refreshing a scoped entry would lose its scope and store the wrong-audience answer.

func (*Store) SetFromResponseWithCut added in v1.7.3

func (s *Store) SetFromResponseWithCut(resp *dns.Msg, keyCD bool, cutUntil time.Time, cutKey uint64)

SetFromResponseWithCut is SetFromResponse plus the delegation identity that supplied cutUntil, retained for the optional Phase-3 generation design.

func (*Store) SetFromResponseWithKey added in v1.6.4

func (s *Store) SetFromResponseWithKey(key uint64, resp *dns.Msg, cutUntil time.Time, cutKey uint64)

SetFromResponseWithKey is the pre-keyed form of SetFromResponse, used by ResponseWriter.WriteMsg, which has the key already.

func (*Store) Stop added in v1.7.4

func (s *Store) Stop()

Stop releases background resources owned by Store-only sub-caches.

type TTLManager added in v1.5.0

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

TTLManager manages TTL calculations.

func NewTTLManager added in v1.5.0

func NewTTLManager(min, max time.Duration) TTLManager

NewTTLManager creates a new TTL manager.

func (TTLManager) Calculate added in v1.5.0

func (tm TTLManager) Calculate(msgTTL time.Duration) time.Duration

(TTLManager).Calculate calculate returns the effective TTL within configured bounds.

Jump to

Keyboard shortcuts

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