Package dns
Domain normalization, syntax validation, DNS pre-flight resolution
checks, multi-resolver consistency cross-checks, static hosts-file
override detection, origin-AS attribution, and SPF/DMARC sender-authority
probes. Sources: internal/dns/dns.go,
internal/dns/resolvers.go, internal/dns/hosts.go,
internal/dns/asn.go, internal/dns/spf.go.
NormalizeDomain
func NormalizeDomain(d string) string |
Lowercases the input and strips scheme, path, port, and trailing dot.
Mirrors the shell normalize_domain helper.
dns.NormalizeDomain("HTTPS://Example.COM:8443/path")
// => "example.com" |
ValidSyntax
func ValidSyntax(d string) bool |
Reports whether d looks like a dotted domain name: labels of
alphanumerics and hyphens with at least one dot. Used to reject malformed
input before any network call.
dns.ValidSyntax("jdsoft.com") // => true
dns.ValidSyntax("no-dot") // => false |
DomainCheck
type DomainCheck struct {
SystemLive bool
PublicLive bool
SystemAddrs []net.IP
PublicAddrs []net.IP
HostsHit bool
HostsIP string
Consistency Consistency
} |
One domain's full probe: system liveness for pre-flight gating, public
liveness for suppression attribution, the aggregate consistency
verdict, the first live A record per side (retained for per-path
interrogation — never scored by raw address), and the hosts-file override
state. The pre-flight gate aborts on system failure and reuses these
outcomes downstream, so each resolver is queried exactly once per
domain.
CheckDomain
func CheckDomain(ctx context.Context, domain string, timeout time.Duration) DomainCheck |
Probes the system resolver and pinned public resolvers once. Use
SystemLive for NXDOMAIN gating and Consistency for
scoring and messaging.
Consistency
type Consistency struct {
Checked bool
Tampered bool
Kind SplitKind
Detail string
} |
Verdict of a multi-resolver cross-check. Detail names the
disagreeing resolvers and the split kind when tampered.
SplitKind
const (
SplitNone SplitKind = iota
SplitSuppression
SplitHorizon
SplitNS
) |
Split direction. Suppression (system dark, public live) signals local
tampering; horizon (system live, public dark) is the expected split-horizon
shape of internal names and never scores; NS marks delegation splits.
CheckConsistency
func CheckConsistency(ctx context.Context, domain string, timeout time.Duration) Consistency |
Queries the system resolver plus pinned 1.1.1.1 and 8.8.8.8 servers in
parallel (stdlib net.Resolver with a pinned dialer; filtered
resolvers excluded by design). Flags NXDOMAIN-vs-live splits and NS
delegation splits; raw A records are never compared, so CDN/GeoDNS churn
stays quiet. Inconclusive resolvers abstain, and fewer than two conclusive
votes is never tampering. Never fails closed.
| Parameter | Description |
| ctx | Parent context; the global 15 second budget. |
| domain | Already-normalized domain to cross-check. |
| timeout | Per-resolver deadline (the CLI --timeout value). |
CheckHostsOverride
func CheckHostsOverride(domain string) (ip string, ok bool) |
Reports a hardcoded static mapping for domain in the OS hosts
database (/etc/hosts on unix,
C:\Windows\System32\drivers\etc\hosts on Windows — see
HostsPath). Such a mapping short-circuits every resolver, so no
cross-check can see it; it is probed once in pre-flight alongside the
resolver queries. Fail-open: unreadable or missing files simply report no
hit, never an error.
ParseHostsFile
func ParseHostsFile(content, domain string) (string, bool) |
Pure scanner behind CheckHostsOverride: ignores comment lines
and inline comments, matches hostnames case-insensitively (trailing-dot
tolerant), first mapping wins (OS resolver precedence). Safe to unit
test.
LookupASN
func LookupASN(ctx context.Context, ip net.IP, timeout time.Duration) (ASNInfo, bool) |
Attributes ip to its origin autonomous system via Team
Cymru's DNS service (<reversed-octets>.origin.asn.cymru.com
TXT), queried through the pinned 1.1.1.1 resolver — never the system
resolver, so a poisoned local resolver cannot attest to its own
redirection. IPv4 only (IPv6 reports not-ok). Fail-open: unanswered or
unparsable queries yield not-ok. Result type:
type ASNInfo struct {
ASN string // origin AS number, e.g. "15169"
Prefix string // announced prefix, e.g. "8.8.8.0/24"
CC string // registrant country code, e.g. "US"
} |
ParseCymruTXT
func ParseCymruTXT(txt string) (ASNInfo, bool) |
Pure parser for one Cymru origin answer
("15169 | 8.8.8.0/24 | US | arin | 1992-12-01"). Companion
helpers: ASNQueryName builds the query name (false for IPv6),
ASNMatch reports a shared origin AS (unknown sides never
match).
CheckSPFAuthority
func CheckSPFAuthority(ctx context.Context, target string, suspectIP net.IP, timeout time.Duration) (SPFResult, bool) |
Evaluates the target's v=spf1 record for the suspect's IP (RFC
7208 mechanism subset: ip4/ip6 CIDRs, a,
mx, recursive include:, redirect=; only a
+-qualifier match authorizes). Recursive includes share the RFC
10-lookup budget; macro mechanisms, multiple v=spf1 records, and
lookup errors all fail open (not-ok). Result type:
type SPFResult struct {
Authorized bool // the IP is covered by a pass-qualifier mechanism
Cover string // the covering mechanism, e.g. "include:_spf.google.com"
} |
CheckDMARC
func CheckDMARC(ctx context.Context, target string, timeout time.Duration) (string, bool) |
Reads the target's DMARC enforcement policy (none,
quarantine, reject) from
_dmarc.<target> TXT through the pinned resolver.
Display-only context (a p=reject target tolerates no
unauthorized senders); fail-open on any problem.
Licensed under the GNU General Public License v3 - Mark Robillard Jr (MARKMENTAL)