package checker import ( "context" "fmt" sdk "git.happydns.org/checker-sdk-go/checker" ) type nsDeclaredRule struct{} func (r *nsDeclaredRule) Name() string { return "authoritative_consistency.ns_declared" } func (r *nsDeclaredRule) Description() string { return "Verifies the service declares at least the recommended number of name servers and that at least one name server could be discovered." } func (r *nsDeclaredRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState { data, errSt := loadObservation(ctx, obs) if errSt != nil { return []sdk.CheckState{*errSt} } minNS := sdk.GetIntOption(opts, "minNameServers", 2) useParentNS := sdk.GetBoolOption(opts, "useParentNS", true) var findings []Finding if len(data.DeclaredNS) == 0 && !useParentNS { findings = append(findings, Finding{ Code: CodeNoNS, Severity: SeverityCrit, Message: "no name servers declared in the service and parent cross-check is disabled", }) } if len(data.Probed) == 0 { findings = append(findings, Finding{ Code: CodeNoNS, Severity: SeverityCrit, Message: "no authoritative name servers could be discovered (declared list empty and parent query empty)", }) } if len(data.DeclaredNS) > 0 && len(data.DeclaredNS) < minNS { findings = append(findings, Finding{ Code: CodeTooFewNS, Severity: SeverityWarn, Message: fmt.Sprintf("only %d name server(s) declared, RFC 1034 recommends at least %d", len(data.DeclaredNS), minNS), }) } if len(findings) == 0 { return []sdk.CheckState{passState("authoritative_consistency.ns_declared.ok", fmt.Sprintf("%d name server(s) declared", len(data.DeclaredNS)))} } return findingsToStates(findings) } type parentDelegationRule struct{} func (r *parentDelegationRule) Name() string { return "authoritative_consistency.parent_delegation" } func (r *parentDelegationRule) Description() string { return "Cross-checks the NS RRset returned by the parent zone's referral with the NS declared in the service." } func (r *parentDelegationRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState { useParentNS := sdk.GetBoolOption(opts, "useParentNS", true) if !useParentNS { return []sdk.CheckState{notTestedState("authoritative_consistency.parent_delegation.skipped", "Parent delegation cross-check disabled by option.")} } data, errSt := loadObservation(ctx, obs) if errSt != nil { return []sdk.CheckState{*errSt} } var findings []Finding if data.ParentQueryError != "" { findings = append(findings, Finding{ Code: CodeParentQueryFailed, Severity: SeverityWarn, Message: fmt.Sprintf("parent delegation query failed: %s", data.ParentQueryError), }) } else if len(data.DeclaredNS) > 0 && len(data.ParentNS) > 0 { missing, extra := diffStringSets(data.DeclaredNS, data.ParentNS) if len(missing) > 0 || len(extra) > 0 { findings = append(findings, Finding{ Code: CodeParentDrift, Severity: SeverityWarn, Message: fmt.Sprintf( "NS RRset at parent does not match declared service: missing=%v extra=%v", missing, extra, ), }) } } if len(findings) == 0 { if len(data.ParentNS) == 0 { return []sdk.CheckState{notTestedState("authoritative_consistency.parent_delegation.skipped", "No parent delegation observed.")} } return []sdk.CheckState{passState("authoritative_consistency.parent_delegation.ok", "Parent delegation matches the declared NS list.")} } return findingsToStates(findings) }