From 2bd19640d964ebd9e8d60f23a1a282652c382b6d Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Tue, 4 Nov 2025 15:51:04 +0000 Subject: [PATCH] feat(health/dns): try another DNS server if one fails --- internal/healthcheck/dns/dns.go | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/internal/healthcheck/dns/dns.go b/internal/healthcheck/dns/dns.go index 13e7d591..e9c3a94c 100644 --- a/internal/healthcheck/dns/dns.go +++ b/internal/healthcheck/dns/dns.go @@ -5,33 +5,60 @@ import ( "errors" "fmt" "net" + "net/netip" + + "github.com/qdm12/dns/v2/pkg/provider" ) // Client is a simple plaintext UDP DNS client, to be used for healthchecks. // Note the client connects to a DNS server only over UDP on port 53, // because we don't want to use DoT or DoH and impact the TCP connections // when running a healthcheck. -type Client struct{} +type Client struct { + serverAddrs []netip.AddrPort + dnsIPIndex int +} func New() *Client { - return &Client{} + return &Client{ + serverAddrs: concatAddrPorts([][]netip.AddrPort{ + provider.Cloudflare().Plain.IPv4, + provider.Google().Plain.IPv4, + provider.Quad9().Plain.IPv4, + provider.OpenDNS().Plain.IPv4, + provider.LibreDNS().Plain.IPv4, + provider.Quadrant().Plain.IPv4, + provider.CiraProtected().Plain.IPv4, + }), + } +} + +func concatAddrPorts(addrs [][]netip.AddrPort) []netip.AddrPort { + var result []netip.AddrPort + for _, addrList := range addrs { + result = append(result, addrList...) + } + return result } var ErrLookupNoIPs = errors.New("no IPs found from DNS lookup") func (c *Client) Check(ctx context.Context) error { + dnsAddr := c.serverAddrs[c.dnsIPIndex].Addr() resolver := &net.Resolver{ PreferGo: true, Dial: func(ctx context.Context, _, _ string) (net.Conn, error) { dialer := net.Dialer{} - return dialer.DialContext(ctx, "udp", "1.1.1.1:53") + return dialer.DialContext(ctx, "udp", dnsAddr.String()) }, } ips, err := resolver.LookupIP(ctx, "ip", "github.com") switch { case err != nil: + c.dnsIPIndex = (c.dnsIPIndex + 1) % len(c.serverAddrs) return err case len(ips) == 0: + c.dnsIPIndex = (c.dnsIPIndex + 1) % len(c.serverAddrs) return fmt.Errorf("%w", ErrLookupNoIPs) default: return nil