check / check (push) Successful in 1m50s
ClassifyTargets kept a name every time it appeared in DNSWATCHER_TARGETS, so example.com,Example.com. put example.com in the domain list twice and every check looked it up and checked its certificates twice, sending two expiry warnings per TLS check (port checks were already grouped by address and port). It now skips a name it has already kept, comparing after the lower-casing and trailing-dot removal it already did; the list keeps the order of first appearance. Model: opus-5-5
91 lines
2.2 KiB
Go
91 lines
2.2 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"golang.org/x/net/publicsuffix"
|
|
)
|
|
|
|
// DNSNameType indicates whether a DNS name is an apex domain or a hostname.
|
|
type DNSNameType int
|
|
|
|
const (
|
|
// DNSNameTypeDomain indicates the name is an apex (eTLD+1) domain.
|
|
DNSNameTypeDomain DNSNameType = iota
|
|
// DNSNameTypeHostname indicates the name is a subdomain/hostname.
|
|
DNSNameTypeHostname
|
|
)
|
|
|
|
// ErrEmptyDNSName is returned when an empty string is passed to ClassifyDNSName.
|
|
var ErrEmptyDNSName = errors.New("empty DNS name")
|
|
|
|
// String returns the string representation of a DNSNameType.
|
|
func (t DNSNameType) String() string {
|
|
switch t {
|
|
case DNSNameTypeDomain:
|
|
return "domain"
|
|
case DNSNameTypeHostname:
|
|
return "hostname"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
// ClassifyDNSName determines whether a DNS name is an apex domain or a
|
|
// hostname (subdomain) using the Public Suffix List. It returns an error
|
|
// if the input is empty or is itself a public suffix (e.g. "co.uk").
|
|
func ClassifyDNSName(name string) (DNSNameType, error) {
|
|
name = strings.ToLower(strings.TrimSuffix(strings.TrimSpace(name), "."))
|
|
|
|
if name == "" {
|
|
return 0, ErrEmptyDNSName
|
|
}
|
|
|
|
etld1, err := publicsuffix.EffectiveTLDPlusOne(name)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("invalid DNS name %q: %w", name, err)
|
|
}
|
|
|
|
if name == etld1 {
|
|
return DNSNameTypeDomain, nil
|
|
}
|
|
|
|
return DNSNameTypeHostname, nil
|
|
}
|
|
|
|
// ClassifyTargets splits a list of DNS names into apex domains and
|
|
// hostnames using the Public Suffix List. It returns an error if any
|
|
// name cannot be classified. A name given more than once, in any letter
|
|
// case or with a trailing dot, is kept once.
|
|
func ClassifyTargets(targets []string) ([]string, []string, error) {
|
|
var domains, hostnames []string
|
|
|
|
seen := make(map[string]bool)
|
|
|
|
for _, t := range targets {
|
|
normalized := strings.ToLower(strings.TrimSuffix(strings.TrimSpace(t), "."))
|
|
|
|
if normalized == "" || seen[normalized] {
|
|
continue
|
|
}
|
|
|
|
seen[normalized] = true
|
|
|
|
typ, classErr := ClassifyDNSName(normalized)
|
|
if classErr != nil {
|
|
return nil, nil, classErr
|
|
}
|
|
|
|
switch typ {
|
|
case DNSNameTypeDomain:
|
|
domains = append(domains, normalized)
|
|
case DNSNameTypeHostname:
|
|
hostnames = append(hostnames, normalized)
|
|
}
|
|
}
|
|
|
|
return domains, hostnames, nil
|
|
}
|