dnswatcher/internal/config/classify.go
clawbot 1db3056594 feat: add PSL-based target classification
Classify DNS names as apex domains or hostnames using the Public
Suffix List (golang.org/x/net/publicsuffix). Correctly handles
multi-level TLDs like .co.uk and .com.au.
2026-02-19 20:09:07 -08:00

62 lines
1.4 KiB
Go

// Package config provides application configuration via Viper.
package config
import (
"fmt"
"strings"
"golang.org/x/net/publicsuffix"
)
// ClassifyTarget determines whether a DNS name is an apex domain
// (eTLD+1) or a hostname (subdomain of an eTLD+1). Returns
// "domain" or "hostname". Returns an error if the name is itself
// a public suffix (e.g. "co.uk") or otherwise invalid.
func ClassifyTarget(name string) (string, error) {
// Normalize: lowercase, strip trailing dot.
name = strings.ToLower(strings.TrimSuffix(name, "."))
if name == "" {
return "", fmt.Errorf("empty target name")
}
apex, err := publicsuffix.EffectiveTLDPlusOne(name)
if err != nil {
return "", fmt.Errorf(
"invalid target %q: %w", name, err,
)
}
if name == apex {
return "domain", nil
}
return "hostname", nil
}
// classifyTargets splits a list of DNS names into apex domains
// and hostnames using the Public Suffix List.
func classifyTargets(
targets []string,
) (domains, hostnames []string, err error) {
for _, target := range targets {
kind, classifyErr := ClassifyTarget(target)
if classifyErr != nil {
return nil, nil, classifyErr
}
switch kind {
case "domain":
domains = append(domains, strings.ToLower(
strings.TrimSuffix(target, "."),
))
case "hostname":
hostnames = append(hostnames, strings.ToLower(
strings.TrimSuffix(target, "."),
))
}
}
return domains, hostnames, nil
}