86 lines
3.3 KiB
Go
86 lines
3.3 KiB
Go
// Package contract defines the DiscoveryEntry types published by
|
|
// checker-dangling and consumed by companion checkers (notably
|
|
// domain-expiry, which subscribes to ExternalTargetType to perform RDAP
|
|
// on the target's registrable domain).
|
|
//
|
|
// This package is deliberately tiny and dependency-light so that any
|
|
// consumer can import it without dragging the whole checker in.
|
|
package contract
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
sdk "git.happydns.org/checker-sdk-go/checker"
|
|
)
|
|
|
|
// ExternalTargetType is the DiscoveryEntry.Type for an out-of-zone
|
|
// pointer target (CNAME/MX/SRV/NS host) whose registrable domain is
|
|
// distinct from the zone apex. Consumers subscribed to this type are
|
|
// expected to look up the registrable domain (RDAP/WHOIS) and publish a
|
|
// "whois" observation per entry Ref.
|
|
const ExternalTargetType = "dangling.external-target.v1"
|
|
|
|
// InZoneTargetType is the DiscoveryEntry.Type for a pointer target that
|
|
// resolves within the same zone or the same registrable domain. It is
|
|
// declared so future probing checkers (ping/http/alias) can subscribe to
|
|
// it for in-zone reachability checks. v1 of checker-dangling does not
|
|
// itself rely on observations attached to in-zone entries.
|
|
const InZoneTargetType = "dangling.in-zone-target.v1"
|
|
|
|
// ExternalTarget is the payload of an ExternalTargetType entry.
|
|
//
|
|
// Owner is the FQDN whose pointer is at risk (e.g. "old-promo.example.com.").
|
|
// Pointer captures the type+target verbatim so a consumer can refer to
|
|
// the precise record when reporting findings. Registrable is the eTLD+1
|
|
// (or PSL-derived equivalent) that an RDAP probe should query.
|
|
type ExternalTarget struct {
|
|
Owner string `json:"owner"`
|
|
Rrtype string `json:"rrtype"` // "CNAME", "MX", "SRV", "NS"
|
|
Target string `json:"target"` // FQDN, no trailing dot
|
|
Registrable string `json:"registrable"`
|
|
}
|
|
|
|
// InZoneTarget mirrors ExternalTarget for in-zone or same-registrable
|
|
// pointers. Registrable is set when known so a subscriber can decide to
|
|
// skip records that point at the user's own domain.
|
|
type InZoneTarget struct {
|
|
Owner string `json:"owner"`
|
|
Rrtype string `json:"rrtype"`
|
|
Target string `json:"target"`
|
|
Registrable string `json:"registrable,omitempty"`
|
|
}
|
|
|
|
// Ref builds the canonical, stable Ref for a (owner, rrtype, target)
|
|
// triple. Callers must use this on both the producer and consumer side
|
|
// so RelatedObservation.Ref correlates with the right entry.
|
|
func Ref(owner, rrtype, target string) string {
|
|
return fmt.Sprintf("%s|%s|%s", owner, rrtype, target)
|
|
}
|
|
|
|
// NewExternalEntry builds a DiscoveryEntry of type ExternalTargetType
|
|
// with the canonical Ref.
|
|
func NewExternalEntry(t ExternalTarget) (sdk.DiscoveryEntry, error) {
|
|
payload, err := json.Marshal(t)
|
|
if err != nil {
|
|
return sdk.DiscoveryEntry{}, fmt.Errorf("marshal external target: %w", err)
|
|
}
|
|
return sdk.DiscoveryEntry{
|
|
Type: ExternalTargetType,
|
|
Ref: Ref(t.Owner, t.Rrtype, t.Target),
|
|
Payload: payload,
|
|
}, nil
|
|
}
|
|
|
|
// NewInZoneEntry builds a DiscoveryEntry of type InZoneTargetType.
|
|
func NewInZoneEntry(t InZoneTarget) (sdk.DiscoveryEntry, error) {
|
|
payload, err := json.Marshal(t)
|
|
if err != nil {
|
|
return sdk.DiscoveryEntry{}, fmt.Errorf("marshal in-zone target: %w", err)
|
|
}
|
|
return sdk.DiscoveryEntry{
|
|
Type: InZoneTargetType,
|
|
Ref: Ref(t.Owner, t.Rrtype, t.Target),
|
|
Payload: payload,
|
|
}, nil
|
|
}
|