Extract querySiblings from observeCoexistence so both CNAME and DNAME coexistence checks share the same parallel RRset scan. Add observeDNAMECoexistence (called from Collect) that populates AliasData.DNAMECoexistence for each DNAME node in DNAMESubstitutions. Add the dname_coexistence rule (RFC 6672 §2.3) that flags any sibling RRsets at a DNAME owner as CRIT, with matching tests.
104 lines
3.5 KiB
Go
104 lines
3.5 KiB
Go
package checker
|
|
|
|
import (
|
|
"encoding/json"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
const ObservationKeyAlias = "alias"
|
|
|
|
type AliasKind string
|
|
|
|
const (
|
|
KindCNAME AliasKind = "CNAME"
|
|
KindDNAME AliasKind = "DNAME"
|
|
KindALIAS AliasKind = "ALIAS" // provider-flattened apex alias, no real RR on the wire
|
|
KindTarget AliasKind = "TARGET"
|
|
)
|
|
|
|
type ChainHop struct {
|
|
Owner string `json:"owner"`
|
|
Kind AliasKind `json:"kind"`
|
|
Target string `json:"target,omitempty"`
|
|
TTL uint32 `json:"ttl,omitempty"`
|
|
Server string `json:"server,omitempty"`
|
|
Synthesized bool `json:"synthesized,omitempty"` // CNAME synthesized from DNAME
|
|
}
|
|
|
|
type CoexistingRRset struct {
|
|
Type string `json:"type"`
|
|
TTL uint32 `json:"ttl,omitempty"`
|
|
}
|
|
|
|
type TerminationReason string
|
|
|
|
const (
|
|
TermOK TerminationReason = "ok"
|
|
TermLoop TerminationReason = "loop"
|
|
TermTooLong TerminationReason = "too_long"
|
|
TermQueryErr TerminationReason = "query_error"
|
|
TermRcode TerminationReason = "rcode"
|
|
)
|
|
|
|
// ChainTermination is always populated after a walk; rules key off Reason.
|
|
type ChainTermination struct {
|
|
Reason TerminationReason `json:"reason"`
|
|
Subject string `json:"subject,omitempty"`
|
|
Detail string `json:"detail,omitempty"`
|
|
Rcode string `json:"rcode,omitempty"` // only with TermRcode
|
|
}
|
|
|
|
// AliasData carries raw facts only; judgement is delegated to the rules.
|
|
type AliasData struct {
|
|
Owner string `json:"owner"`
|
|
|
|
// Apex is empty iff the apex lookup failed; ApexLookupError explains why.
|
|
Apex string `json:"apex,omitempty"`
|
|
ApexLookupError string `json:"apex_lookup_error,omitempty"`
|
|
AuthServers []string `json:"auth_servers,omitempty"`
|
|
|
|
Chain []ChainHop `json:"chain,omitempty"`
|
|
ChainTerminated ChainTermination `json:"chain_terminated"`
|
|
|
|
// FinalTarget is the last name in the chain, equal to Owner when there is
|
|
// no indirection.
|
|
FinalTarget string `json:"final_target,omitempty"`
|
|
FinalA []string `json:"final_a,omitempty"`
|
|
FinalAAAA []string `json:"final_aaaa,omitempty"`
|
|
FinalRcode string `json:"final_rcode,omitempty"`
|
|
|
|
// Coexisting is populated only when Owner has a CNAME.
|
|
Coexisting []CoexistingRRset `json:"coexisting,omitempty"`
|
|
// DNAMECoexistence maps each DNAME owner (from DNAMESubstitutions) to its sibling RRsets.
|
|
DNAMECoexistence map[string][]CoexistingRRset `json:"dname_coexistence,omitempty"`
|
|
|
|
OwnerIsApex bool `json:"owner_is_apex,omitempty"`
|
|
OwnerHasCNAME bool `json:"owner_has_cname,omitempty"`
|
|
|
|
// Apex* fields are populated only when OwnerIsApex.
|
|
ApexHasA bool `json:"apex_has_a,omitempty"`
|
|
ApexHasAAAA bool `json:"apex_has_aaaa,omitempty"`
|
|
ApexHasCNAME bool `json:"apex_has_cname,omitempty"`
|
|
ApexFlattening bool `json:"apex_flattening,omitempty"`
|
|
|
|
ZoneSigned bool `json:"zone_signed,omitempty"`
|
|
// CNAMESigCheckDone gates CNAMESigned: a false here means we never probed
|
|
// (zone unsigned or no CNAME), so CNAMESigned must not be interpreted.
|
|
CNAMESigCheckDone bool `json:"cname_sig_check_done,omitempty"`
|
|
CNAMESigned bool `json:"cname_signed,omitempty"`
|
|
|
|
DNAMESubstitutions []ChainHop `json:"dname_substitutions,omitempty"`
|
|
}
|
|
|
|
// cnameService mirrors happyDomain's svcs.CNAME / svcs.SpecialCNAME wire shape.
|
|
type cnameService struct {
|
|
Record *dns.CNAME `json:"cname"`
|
|
}
|
|
|
|
// serviceMessage mirrors happyDomain's ServiceMessage envelope.
|
|
type serviceMessage struct {
|
|
Type string `json:"_svctype"`
|
|
Domain string `json:"_domain"`
|
|
Service json.RawMessage `json:"Service"`
|
|
}
|