package checker import ( "encoding/json" "net" "strconv" "strings" "time" sdk "git.happydns.org/checker-sdk-go/checker" ) // tlsProbeView is a permissive subset of checker-tls's probe payload; // only fields the CAA rule needs are decoded so the TLS checker can // evolve its schema independently. type tlsProbeView struct { Host string `json:"host,omitempty"` Port uint16 `json:"port,omitempty"` Endpoint string `json:"endpoint,omitempty"` Type string `json:"type,omitempty"` Issuer string `json:"issuer,omitempty"` IssuerDN string `json:"issuer_dn,omitempty"` IssuerAKI string `json:"issuer_aki,omitempty"` NotAfter time.Time `json:"not_after,omitempty"` ChainValid *bool `json:"chain_valid,omitempty"` DNSNames []string `json:"dns_names,omitempty"` Subject string `json:"subject,omitempty"` } // isWildcard reports whether the observed certificate covers at least // one wildcard DNS name. Used to pick between the CAA "issue" and // "issuewild" allow lists per RFC 8659 §4.3. func (v *tlsProbeView) isWildcard() bool { for _, n := range v.DNSNames { if strings.HasPrefix(n, "*.") { return true } } return false } func (v *tlsProbeView) address() string { if v.Endpoint != "" { return v.Endpoint } if v.Host != "" && v.Port != 0 { return net.JoinHostPort(v.Host, strconv.FormatUint(uint64(v.Port), 10)) } return v.Host } // parseTLSRelated decodes a RelatedObservation into probes. Two // payload shapes are accepted: the current {"probes": {ref: …}} map // (filtered by r.Ref when set) and a bare top-level probe (back-compat). // Returns nil when the payload is not a recognizable probe shape. func parseTLSRelated(r sdk.RelatedObservation) []*tlsProbeView { var keyed struct { Probes map[string]tlsProbeView `json:"probes"` } if err := json.Unmarshal(r.Data, &keyed); err == nil && keyed.Probes != nil { if r.Ref != "" { if p, ok := keyed.Probes[r.Ref]; ok { cp := p return []*tlsProbeView{&cp} } } out := make([]*tlsProbeView, 0, len(keyed.Probes)) for _, p := range keyed.Probes { cp := p out = append(out, &cp) } return out } var v tlsProbeView if err := json.Unmarshal(r.Data, &v); err != nil { return nil } if v.Host == "" && v.IssuerAKI == "" && v.IssuerDN == "" { return nil } return []*tlsProbeView{&v} } // parseAllTLSRelated flattens a slice of RelatedObservations into one // entry per endpoint. func parseAllTLSRelated(related []sdk.RelatedObservation) []*tlsProbeView { var out []*tlsProbeView for _, r := range related { out = append(out, parseTLSRelated(r)...) } return out }