check / check (push) Successful in 1m21s
The watcher tests used a stand-in resolver and the resolver timeout test a stand-in DNS client, against the rule that DNS is never mocked. Watcher tests that look something up in DNS now run the real resolver against live servers, each attempt on a new watcher. A DNS change is tested by saving values live DNS never returns (names under .invalid, 192.0.2.1) in the state a check starts from, or by marking a real nameserver failed. The timeout test queries 192.0.2.1, where nothing answers. The live-DNS retry and concurrency limit moved from the resolver tests to internal/livedns, so both packages share them. NewFromLoggerWithClient had no other use and is gone. TESTING.md now states the README's rule. Model: opus-5-5
71 lines
1.7 KiB
Go
71 lines
1.7 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.
|
|
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
|
|
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.
|