package probe import ( "context" "fmt" "net" "net/netip" "time" ) // DNSResult contains the outcome of one DNS lookup. type DNSResult struct { Reachable bool Duration time.Duration IPs []net.IP Err error } // Resolve checks whether a domain can be resolved against a specific DNS server. func Resolve(ctx context.Context, server string, domain string, timeout time.Duration) DNSResult { start := time.Now() lookupCtx, cancel := context.WithTimeout(ctx, timeout) defer cancel() dialer := net.Dialer{Timeout: timeout} resolver := net.Resolver{ PreferGo: true, Dial: func(ctx context.Context, network string, address string) (net.Conn, error) { conn, err := dialer.DialContext(ctx, network, dnsServerAddress(server)) if err != nil { return nil, fmt.Errorf("dialing dns server %s: %w", server, err) } return conn, nil }, } ips, err := resolver.LookupIP(lookupCtx, "ip", domain) duration := time.Since(start) if err != nil { return DNSResult{Duration: duration, Err: fmt.Errorf("resolving %s with %s: %w", domain, server, err)} } return DNSResult{Reachable: len(ips) > 0, Duration: duration, IPs: ips} } func dnsServerAddress(server string) string { if _, err := netip.ParseAddrPort(server); err == nil { return server } if addr, err := netip.ParseAddr(server); err == nil { return net.JoinHostPort(addr.String(), "53") } return net.JoinHostPort(server, "53") }