Files
dnswatcher/internal/resolver/resolver.go
sneak 483bed68a1 Add resolver API definition and comprehensive live-DNS test suite
The test suite defines the full resolver contract using live DNS
queries against controlled records in the sneak.cloud zone
(Cloudflare). Covers FindAuthoritativeNameservers, QueryNameserver,
QueryAllNameservers, LookupNS, and ResolveIPAddresses, including
sorting/determinism guarantees, trailing-dot handling, per-NS
response status model, lame-delegation detection, NXDOMAIN
semantics, CNAME following, and context cancellation.

Also adds DNSSEC validation to planned future features in README.
2026-07-07 03:06:49 +02:00

115 lines
3.0 KiB
Go

// Package resolver provides iterative DNS resolution from root nameservers.
// It traces the full delegation chain from IANA root servers through TLD
// and domain nameservers, never relying on upstream recursive resolvers.
package resolver
import (
"context"
"log/slog"
"go.uber.org/fx"
"sneak.berlin/go/dnswatcher/internal/logger"
)
// Query status constants matching the state model.
const (
StatusOK = "ok"
StatusError = "error"
StatusNXDomain = "nxdomain"
StatusNoData = "nodata"
)
// MaxCNAMEDepth is the maximum CNAME chain depth to follow.
const MaxCNAMEDepth = 10
// Params contains dependencies for Resolver.
type Params struct {
fx.In
Logger *logger.Logger
}
// NameserverResponse holds one nameserver's response for a query.
type NameserverResponse struct {
Nameserver string
Records map[string][]string
Status string
Error string
}
// Resolver performs iterative DNS resolution from root servers.
type Resolver struct {
log *slog.Logger
}
// New creates a new Resolver instance for use with uber/fx.
func New(
_ fx.Lifecycle,
params Params,
) (*Resolver, error) {
return &Resolver{
log: params.Logger.Get(),
}, nil
}
// NewFromLogger creates a Resolver directly from an slog.Logger,
// useful for testing without the fx lifecycle.
func NewFromLogger(log *slog.Logger) *Resolver {
return &Resolver{log: log}
}
// FindAuthoritativeNameservers traces the delegation chain from
// root servers to discover all authoritative nameservers for the
// given domain. Returns the NS hostnames (e.g. ["ns1.example.com.",
// "ns2.example.com."]).
func (r *Resolver) FindAuthoritativeNameservers(
_ context.Context,
_ string,
) ([]string, error) {
return nil, ErrNotImplemented
}
// QueryNameserver queries a specific authoritative nameserver for
// all supported record types (A, AAAA, CNAME, MX, TXT, SRV, CAA,
// NS) for the given hostname. Returns a NameserverResponse with
// per-type record slices and a status indicating success or the
// type of failure.
func (r *Resolver) QueryNameserver(
_ context.Context,
_ string,
_ string,
) (*NameserverResponse, error) {
return nil, ErrNotImplemented
}
// QueryAllNameservers discovers the authoritative nameservers for
// the hostname's parent domain, then queries each one independently.
// Returns a map from nameserver hostname to its response.
func (r *Resolver) QueryAllNameservers(
_ context.Context,
_ string,
) (map[string]*NameserverResponse, error) {
return nil, ErrNotImplemented
}
// LookupNS returns the NS record set for a domain by performing
// iterative resolution. This is used for domain (apex) monitoring.
func (r *Resolver) LookupNS(
_ context.Context,
_ string,
) ([]string, error) {
return nil, ErrNotImplemented
}
// ResolveIPAddresses resolves a hostname to all IPv4 and IPv6
// addresses by querying all authoritative nameservers and following
// CNAME chains up to MaxCNAMEDepth. Returns a deduplicated list
// of IP address strings.
func (r *Resolver) ResolveIPAddresses(
_ context.Context,
_ string,
) ([]string, error) {
return nil, ErrNotImplemented
}