checker: define Resolution verdict constants to make the collect/evaluate contract explicit
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
355f2b92eb
commit
62e85a155e
3 changed files with 23 additions and 12 deletions
|
|
@ -76,7 +76,7 @@ func (p *danglingProvider) Collect(ctx context.Context, opts sdk.CheckerOptions)
|
||||||
seen[key] = true
|
seen[key] = true
|
||||||
classifyExternal(&pt, zoneRegistrable)
|
classifyExternal(&pt, zoneRegistrable)
|
||||||
if skipResolution {
|
if skipResolution {
|
||||||
pt.Resolution = "skipped"
|
pt.Resolution = ResolutionSkipped
|
||||||
} else {
|
} else {
|
||||||
pt.Resolution, pt.ResolutionDetail = resolveHost(ctx, pt.Target)
|
pt.Resolution, pt.ResolutionDetail = resolveHost(ctx, pt.Target)
|
||||||
}
|
}
|
||||||
|
|
@ -287,7 +287,7 @@ func classifyExternal(pt *Pointer, zoneRegistrable string) {
|
||||||
func defaultResolveHost(ctx context.Context, target string) (verdict, detail string) {
|
func defaultResolveHost(ctx context.Context, target string) (verdict, detail string) {
|
||||||
target = strings.TrimSuffix(target, ".")
|
target = strings.TrimSuffix(target, ".")
|
||||||
if target == "" {
|
if target == "" {
|
||||||
return "skipped", "empty target"
|
return ResolutionSkipped, "empty target"
|
||||||
}
|
}
|
||||||
cctx, cancel := context.WithTimeout(ctx, resolverTimeout)
|
cctx, cancel := context.WithTimeout(ctx, resolverTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
@ -295,25 +295,25 @@ func defaultResolveHost(ctx context.Context, target string) (verdict, detail str
|
||||||
ips, err := net.DefaultResolver.LookupHost(cctx, target)
|
ips, err := net.DefaultResolver.LookupHost(cctx, target)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if len(ips) == 0 {
|
if len(ips) == 0 {
|
||||||
return "no_answer", ""
|
return ResolutionNoAnswer, ""
|
||||||
}
|
}
|
||||||
return "ok", ""
|
return ResolutionOK, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
var dnsErr *net.DNSError
|
var dnsErr *net.DNSError
|
||||||
if errors.As(err, &dnsErr) {
|
if errors.As(err, &dnsErr) {
|
||||||
switch {
|
switch {
|
||||||
case dnsErr.IsNotFound:
|
case dnsErr.IsNotFound:
|
||||||
return "nxdomain", dnsErr.Err
|
return ResolutionNXDomain, dnsErr.Err
|
||||||
case dnsErr.IsTimeout:
|
case dnsErr.IsTimeout:
|
||||||
return "timeout", dnsErr.Err
|
return ResolutionTimeout, dnsErr.Err
|
||||||
case strings.Contains(strings.ToLower(dnsErr.Err), "servfail"):
|
case strings.Contains(strings.ToLower(dnsErr.Err), "servfail"):
|
||||||
return "servfail", dnsErr.Err
|
return ResolutionServFail, dnsErr.Err
|
||||||
default:
|
default:
|
||||||
return "error", dnsErr.Err
|
return ResolutionError, dnsErr.Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "error", err.Error()
|
return ResolutionError, err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ownerFQDN returns the record owner FQDN, preferring the service's _domain field over subdomain+apex.
|
// ownerFQDN returns the record owner FQDN, preferring the service's _domain field over subdomain+apex.
|
||||||
|
|
|
||||||
|
|
@ -141,19 +141,19 @@ func evaluatePointer(pt *Pointer, whoisByRef map[string]*whoisFacts) []SignalTri
|
||||||
var out []SignalTrigger
|
var out []SignalTrigger
|
||||||
|
|
||||||
switch pt.Resolution {
|
switch pt.Resolution {
|
||||||
case "nxdomain":
|
case ResolutionNXDomain:
|
||||||
out = append(out, SignalTrigger{
|
out = append(out, SignalTrigger{
|
||||||
Rrtype: pt.Rrtype, Target: pt.Target,
|
Rrtype: pt.Rrtype, Target: pt.Target,
|
||||||
Reason: "Target does not resolve (NXDOMAIN). The record points at a host that no longer exists.",
|
Reason: "Target does not resolve (NXDOMAIN). The record points at a host that no longer exists.",
|
||||||
Detail: pt.ResolutionDetail, Severity: SeverityCrit,
|
Detail: pt.ResolutionDetail, Severity: SeverityCrit,
|
||||||
})
|
})
|
||||||
case "servfail":
|
case ResolutionServFail:
|
||||||
out = append(out, SignalTrigger{
|
out = append(out, SignalTrigger{
|
||||||
Rrtype: pt.Rrtype, Target: pt.Target,
|
Rrtype: pt.Rrtype, Target: pt.Target,
|
||||||
Reason: "Target lookup returned SERVFAIL. The authoritative server may be misconfigured or the delegation broken.",
|
Reason: "Target lookup returned SERVFAIL. The authoritative server may be misconfigured or the delegation broken.",
|
||||||
Detail: pt.ResolutionDetail, Severity: SeverityWarn,
|
Detail: pt.ResolutionDetail, Severity: SeverityWarn,
|
||||||
})
|
})
|
||||||
case "no_answer":
|
case ResolutionNoAnswer:
|
||||||
out = append(out, SignalTrigger{
|
out = append(out, SignalTrigger{
|
||||||
Rrtype: pt.Rrtype, Target: pt.Target,
|
Rrtype: pt.Rrtype, Target: pt.Target,
|
||||||
Reason: "Target resolves to no address (NOERROR with empty answer). Rarely the operator's intent for a pointer record.",
|
Reason: "Target resolves to no address (NOERROR with empty answer). Rarely the operator's intent for a pointer record.",
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,17 @@ import (
|
||||||
|
|
||||||
const ObservationKeyDangling = "dangling_records"
|
const ObservationKeyDangling = "dangling_records"
|
||||||
|
|
||||||
|
// Resolution verdict constants — the shared contract between Collect and Evaluate.
|
||||||
|
const (
|
||||||
|
ResolutionOK = "ok"
|
||||||
|
ResolutionNXDomain = "nxdomain"
|
||||||
|
ResolutionServFail = "servfail"
|
||||||
|
ResolutionNoAnswer = "no_answer"
|
||||||
|
ResolutionTimeout = "timeout"
|
||||||
|
ResolutionError = "error"
|
||||||
|
ResolutionSkipped = "skipped"
|
||||||
|
)
|
||||||
|
|
||||||
// DanglingData is the raw observation payload; one Pointer per (owner, rrtype, target) triple.
|
// DanglingData is the raw observation payload; one Pointer per (owner, rrtype, target) triple.
|
||||||
type DanglingData struct {
|
type DanglingData struct {
|
||||||
Zone string `json:"zone,omitempty"`
|
Zone string `json:"zone,omitempty"`
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue