Files
dnswatcher/internal/resolver/resolver.go
T
sneak 0ed7667ef5
check / check (push) Canceled after 0s
resolver, watcher: a record type whose query fails keeps its previous records (closes #231)
The resolver lists in FailedTypes each record type whose query to a
nameserver got no usable reply: none after two tries, an error reply, a
referral, or a truncated reply whose TCP retry failed. Records holds
nothing for such a type, never none or the part that fit. A nameserver
that answered no type has failed, as before. The watcher keeps the
previous check's records for a failed type; when that check did not know
them either (first check, new or failing nameserver), the type is saved
in failedTypes and left out of record and inconsistency comparisons.
ResolveIPAddresses no longer counts a nameserver whose A, AAAA or CNAME
query failed as an answer.

Model: opus-5-5
2026-10-02 06:00:30 +00:00

75 lines
1.9 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 (
"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"
StatusTimeout = "timeout"
)
// 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.
// FailedTypes lists the record types whose query got no usable reply,
// and Records holds nothing for them: their records are not known. When
// no record type got one, Status and Error say the nameserver failed.
type NameserverResponse struct {
Nameserver string
Records map[string][]string
FailedTypes []string
Status string
Error string
}
// Resolver performs iterative DNS resolution from root servers.
type Resolver struct {
log *slog.Logger
client DNSClient
tcp DNSClient
}
// 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(),
client: &udpClient{timeout: queryTimeoutDuration},
tcp: &tcpClient{timeout: queryTimeoutDuration},
}, 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,
client: &udpClient{timeout: queryTimeoutDuration},
tcp: &tcpClient{timeout: queryTimeoutDuration},
}
}
// Method implementations are in iterative.go.