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"` }