148 lines
5.3 KiB
Go
148 lines
5.3 KiB
Go
package checker
|
|
|
|
import (
|
|
"encoding/json"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
// ObservationKeyAlias is the observation key for alias data.
|
|
const ObservationKeyAlias = "alias"
|
|
|
|
// AliasKind identifies the flavour of indirection involved in a hop.
|
|
type AliasKind string
|
|
|
|
const (
|
|
KindCNAME AliasKind = "CNAME"
|
|
KindDNAME AliasKind = "DNAME"
|
|
KindALIAS AliasKind = "ALIAS" // provider-flattened apex alias (pseudo-record)
|
|
KindTarget AliasKind = "TARGET"
|
|
)
|
|
|
|
// ChainHop represents one step of the resolution chain.
|
|
type ChainHop struct {
|
|
Owner string `json:"owner"`
|
|
Kind AliasKind `json:"kind"`
|
|
Target string `json:"target,omitempty"`
|
|
TTL uint32 `json:"ttl,omitempty"`
|
|
// Server is the authoritative server that answered for this hop.
|
|
Server string `json:"server,omitempty"`
|
|
// Synthesized is true when this hop is a CNAME synthesized from a DNAME.
|
|
Synthesized bool `json:"synthesized,omitempty"`
|
|
}
|
|
|
|
// CoexistingRRset records an RRset that sits next to a CNAME at the same owner.
|
|
type CoexistingRRset struct {
|
|
Type string `json:"type"`
|
|
TTL uint32 `json:"ttl,omitempty"`
|
|
}
|
|
|
|
// TerminationReason classifies why a chain walk stopped.
|
|
type TerminationReason string
|
|
|
|
const (
|
|
TermOK TerminationReason = "ok"
|
|
TermLoop TerminationReason = "loop"
|
|
TermTooLong TerminationReason = "too_long"
|
|
TermQueryErr TerminationReason = "query_error"
|
|
TermRcode TerminationReason = "rcode"
|
|
)
|
|
|
|
// ChainTermination records why the chain walk stopped. It is always populated
|
|
// once the walk returns; rules key off Reason to decide whether they fire.
|
|
type ChainTermination struct {
|
|
Reason TerminationReason `json:"reason"`
|
|
Subject string `json:"subject,omitempty"`
|
|
Detail string `json:"detail,omitempty"`
|
|
// Rcode is populated only when Reason == TermRcode.
|
|
Rcode string `json:"rcode,omitempty"`
|
|
}
|
|
|
|
// AliasData is the observation payload persisted by the checker. It carries
|
|
// raw facts only: rules are the sole judges.
|
|
type AliasData struct {
|
|
// Owner is the name we started resolving from (FQDN).
|
|
Owner string `json:"owner"`
|
|
|
|
// Apex is the zone apex of Owner (where SOA lives). Empty iff the
|
|
// apex lookup failed; ApexLookupError explains why.
|
|
Apex string `json:"apex,omitempty"`
|
|
|
|
// ApexLookupError, when non-empty, captures why findApex failed.
|
|
ApexLookupError string `json:"apex_lookup_error,omitempty"`
|
|
|
|
// AuthServers are the authoritative servers of the apex zone.
|
|
AuthServers []string `json:"auth_servers,omitempty"`
|
|
|
|
// Chain is the ordered list of hops from Owner down to the final
|
|
// resolvable (or unresolvable) target.
|
|
Chain []ChainHop `json:"chain,omitempty"`
|
|
|
|
// ChainTerminated records why the walk stopped.
|
|
ChainTerminated ChainTermination `json:"chain_terminated"`
|
|
|
|
// FinalTarget is the last name in the chain (possibly Owner itself when
|
|
// there is no indirection).
|
|
FinalTarget string `json:"final_target,omitempty"`
|
|
|
|
// FinalA / FinalAAAA hold the addresses that the chain ultimately resolves
|
|
// to. Empty when the target does not produce any address.
|
|
FinalA []string `json:"final_a,omitempty"`
|
|
FinalAAAA []string `json:"final_aaaa,omitempty"`
|
|
|
|
// FinalRcode is the textual rcode of the final A/AAAA lookups (e.g.
|
|
// "NOERROR", "NXDOMAIN", "SERVFAIL"); empty when not applicable.
|
|
FinalRcode string `json:"final_rcode,omitempty"`
|
|
|
|
// Coexisting lists RRsets that share the owner with a CNAME. Populated
|
|
// only when a CNAME is present at Owner.
|
|
Coexisting []CoexistingRRset `json:"coexisting,omitempty"`
|
|
|
|
// OwnerIsApex is true when the queried name is the zone apex.
|
|
OwnerIsApex bool `json:"owner_is_apex,omitempty"`
|
|
|
|
// OwnerHasCNAME is true when a CNAME record exists at Owner.
|
|
OwnerHasCNAME bool `json:"owner_has_cname,omitempty"`
|
|
|
|
// ApexHasA / ApexHasAAAA record the presence of A/AAAA at the apex
|
|
// (populated only when OwnerIsApex).
|
|
ApexHasA bool `json:"apex_has_a,omitempty"`
|
|
ApexHasAAAA bool `json:"apex_has_aaaa,omitempty"`
|
|
|
|
// ApexHasCNAME is true when a CNAME literally sits at the apex.
|
|
ApexHasCNAME bool `json:"apex_has_cname,omitempty"`
|
|
|
|
// ApexFlattening is true when the apex returns A/AAAA alongside SOA/NS
|
|
// without a CNAME (classic ALIAS/ANAME provider-side flattening).
|
|
ApexFlattening bool `json:"apex_flattening,omitempty"`
|
|
|
|
// ZoneSigned reports whether the apex has DNSKEY records (DNSSEC signed).
|
|
ZoneSigned bool `json:"zone_signed,omitempty"`
|
|
|
|
// CNAMESigCheckDone is true when the DO-bit probe that verifies the
|
|
// CNAME's RRSIG actually ran (i.e. the zone was signed and a CNAME was
|
|
// present to probe).
|
|
CNAMESigCheckDone bool `json:"cname_sig_check_done,omitempty"`
|
|
|
|
// CNAMESigned reports whether the CNAME hop at Owner carries an RRSIG.
|
|
CNAMESigned bool `json:"cname_signed,omitempty"`
|
|
|
|
// DNAMESubstitutions records any DNAME record encountered above Owner
|
|
// that rewrote the name during resolution.
|
|
DNAMESubstitutions []ChainHop `json:"dname_substitutions,omitempty"`
|
|
}
|
|
|
|
// cnameService is the minimal local mirror of happyDomain's `svcs.CNAME` and
|
|
// `svcs.SpecialCNAME` types. Both carry a single *dns.CNAME under the key
|
|
// "cname". github.com/miekg/dns marshals it in the shape happyDomain uses.
|
|
type cnameService struct {
|
|
Record *dns.CNAME `json:"cname"`
|
|
}
|
|
|
|
// serviceMessage is the minimal local mirror of happyDomain's ServiceMessage
|
|
// envelope.
|
|
type serviceMessage struct {
|
|
Type string `json:"_svctype"`
|
|
Domain string `json:"_domain"`
|
|
Service json.RawMessage `json:"Service"`
|
|
}
|