check / check (push) Waiting to run
The resolver lists in FailedTypes each record type whose query to a nameserver got no usable reply (no reply, a code other than NOERROR or NXDOMAIN, a referral, or a truncated reply whose TCP retry failed) and logs it unless shutdown cut it short. A nameserver that answered no type has failed. The watcher saves such a type in failedTypes with the previous check's records, leaves it out of the comparison with other nameservers on that check, and compares it with the next answer. When the previous check did not know its records either, it is also in unknownTypes and not compared until it answers. A nameserver whose A, AAAA or CNAME query failed is no answer when following a CNAME or resolving addresses. Model: opus-5-5
75 lines
1.9 KiB
Go
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.
|