// This file is part of the happyDomain (R) project. // Copyright (c) 2020-2026 happyDomain // Authors: Pierre-Olivier Mercier, et al. package checker import ( "context" "encoding/json" "fmt" "net" sdk "git.happydns.org/checker-sdk-go/checker" happydns "git.happydns.org/happyDomain/model" "git.happydns.org/happyDomain/services/abstract" ) // resolver is the *net.Resolver used to discover additional A/AAAA records // beyond what abstract.Server pins. Overridable in tests. var resolver = net.DefaultResolver // resolveServer extracts the *abstract.Server payload from the options. func resolveServer(opts sdk.CheckerOptions) (*abstract.Server, error) { svc, ok := sdk.GetOption[happydns.ServiceMessage](opts, OptionService) if !ok { return nil, fmt.Errorf("no service in options: did the host wire AutoFillService?") } if svc.Type != "abstract.Server" { return nil, fmt.Errorf("service is %q, expected abstract.Server", svc.Type) } var server abstract.Server if err := json.Unmarshal(svc.Service, &server); err != nil { return nil, fmt.Errorf("unmarshal abstract.Server: %w", err) } return &server, nil } // addressesFromServer returns the (host, ips) tuple to probe. origin is // the service's parent zone; the A/AAAA Hdr.Name is relative to it as // happyDomain encodes service owners, so we must join before using as FQDN. func addressesFromServer(server *abstract.Server, origin string) (host string, ips []string) { if server.A != nil && len(server.A.A) > 0 { host = sdk.JoinRelative(server.A.Hdr.Name, origin) ips = append(ips, server.A.A.String()) } if server.AAAA != nil && len(server.AAAA.AAAA) > 0 { if host == "" { host = sdk.JoinRelative(server.AAAA.Hdr.Name, origin) } ips = append(ips, server.AAAA.AAAA.String()) } return } // discoverIPs resolves host through the system resolver and returns every // A/AAAA address it knows about. abstract.Server only carries one pinned A // and one pinned AAAA, so a domain backed by multiple records would only // be partially probed without this. // // Failures are non-fatal: callers fall back to the pinned IPs from // addressesFromServer. Returned IPs are deduped against `seen`. func discoverIPs(ctx context.Context, host string, seen map[string]struct{}) []string { if host == "" { return nil } addrs, err := resolver.LookupIP(ctx, "ip", host) if err != nil { return nil } var out []string for _, ip := range addrs { s := ip.String() if _, dup := seen[s]; dup { continue } seen[s] = struct{}{} out = append(out, s) } return out }