// This file is part of the happyDomain (R) project. // Copyright (c) 2026 happyDomain // Authors: Pierre-Olivier Mercier, et al. // // This program is offered under a commercial and under the AGPL license. // For commercial licensing, contact us at . // // For AGPL licensing: // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. package checker import ( "encoding/json" "github.com/miekg/dns" ) // ObservationKeyAlias is the observation key for alias data. const ObservationKeyAlias = "alias" // Severity classifies a finding emitted by the alias checker. type Severity string const ( SeverityInfo Severity = "info" SeverityWarn Severity = "warn" SeverityCrit Severity = "crit" ) // 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"` } // AliasFinding describes a single observation produced while running // the alias testsuite. type AliasFinding struct { Code string `json:"code"` Severity Severity `json:"severity"` Message string `json:"message"` // Subject names the owner/target the finding applies to. Subject string `json:"subject,omitempty"` // Hint is a short remediation suggestion, surfaced by the HTML report. Hint string `json:"hint,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"` } // AliasData is the observation payload persisted by the checker. 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). Apex string `json:"apex,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"` // 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"` // Rcode is the textual rcode of the final lookup (e.g. "NOERROR", // "NXDOMAIN", "SERVFAIL"); empty when not applicable. Rcode string `json:"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"` // ApexFlattening is true when the apex returns A/AAAA alongside SOA/NS // (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"` // 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"` // Findings is the full list of issues produced by the run. Findings []AliasFinding `json:"findings"` } // 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"` }