From 70ee5660e133cedb64cd3942fdb04582c9fbf00e Mon Sep 17 00:00:00 2001 From: cyBerta <cyberta@riseup.net> Date: Tue, 15 Apr 2025 03:22:24 +0200 Subject: [PATCH] fix index out of range panic in case doh resolution returns empty A record slice. While being at it add IPv6 resolution to DoH implementation --- pkg/bootstrap/doh.go | 35 +++++++++++++++++++++++++++++++++-- pkg/bootstrap/doh_test.go | 16 ++++++++++++++++ pkg/bootstrap/utils.go | 13 ++++++++++--- 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/pkg/bootstrap/doh.go b/pkg/bootstrap/doh.go index d56bcf0..b96ec9a 100644 --- a/pkg/bootstrap/doh.go +++ b/pkg/bootstrap/doh.go @@ -36,16 +36,47 @@ func dohQuery(domain string) (string, error) { HTTPClient: &http.Client{Timeout: 10 * time.Second}, } + // lookup A records for IPv4 ips, _, err := resolver.LookupA(domain) if err != nil { log.Warn(). Str("resolver", dnsServer). Str("domain", domain). Err(err). - Msg("Could not resolve host with DNS over HTTPs") + Msg("Could not resolve host's IPv4 address with DNS over HTTPS") continue } - return ips[0].IP4, nil + + if len(ips) > 0 { + return ips[0].IP4, nil + } + + // fallback: lookup AAAA records for IPv6 + log.Warn(). + Str("resolver", dnsServer). + Str("domain", domain). + Err(err). + Msg("No A records found for domain") + + v6Ips, _, err := resolver.LookupAAAA(domain) + if err != nil { + log.Warn(). + Str("resolver", dnsServer). + Str("domain", domain). + Err(err). + Msg("Could not resolve host's IPv6 address with DNS over HTTPS") + continue + } + + if len(v6Ips) > 0 { + return v6Ips[0].IP6, nil + } + + log.Warn(). + Str("resolver", dnsServer). + Str("domain", domain). + Err(err). + Msg("No AAAA records found for domain") } return "", errors.New("Could not resolve ip with DNS over HTTPS. Tried all resolvers") diff --git a/pkg/bootstrap/doh_test.go b/pkg/bootstrap/doh_test.go index 4f01b58..1a7d83e 100644 --- a/pkg/bootstrap/doh_test.go +++ b/pkg/bootstrap/doh_test.go @@ -2,6 +2,7 @@ package bootstrap import ( "os" + "strings" "testing" "github.com/rs/zerolog" @@ -18,4 +19,19 @@ func TestDoh(t *testing.T) { ip, err := dohQuery("leap.se") assert.NoError(t, err, "dohQuery failed") assert.NotNil(t, ip, "ip should not be nil") + assert.NotEmpty(t, ip, "ip should not be empty") +} + +func TestDohHandleEmptyRecords(t *testing.T) { + ip, err := dohQuery("notexising-kjhfdfghfhjgiuzuzfgfcdxfsa.org") + assert.Error(t, err, "dohQuery failed") + assert.Empty(t, ip, "ip should be empty") +} + +func TestDohHandleAAAARecords(t *testing.T) { + ip, err := dohQuery("ipv6.google.com") + assert.NoError(t, err, "dohQuery failed") + assert.NotNil(t, ip, "ip should not be nil") + assert.NotEmpty(t, ip, "ip should not be empty") + assert.Equal(t, true, strings.Contains(ip, ":") && !strings.Contains(ip, ".")) } diff --git a/pkg/bootstrap/utils.go b/pkg/bootstrap/utils.go index 4347bca..871231d 100644 --- a/pkg/bootstrap/utils.go +++ b/pkg/bootstrap/utils.go @@ -7,6 +7,7 @@ import ( "net/http" "net/url" "strconv" + "strings" "time" bitmask_storage "0xacab.org/leap/bitmask-core/pkg/storage" @@ -57,16 +58,22 @@ func (c *Config) getAPIClient() *http.Client { Str("domain", addr). Msg("Resolving host with DNS over HTTPs") - ip4, err := dohQuery(c.Host) + ip, err := dohQuery(c.Host) if err != nil { return nil, err } log.Debug(). Str("domain", addr). - Str("ip4", ip4). + Str("ip", ip). Msg("Sucessfully resolved host via DNS over HTTPs") - addr = fmt.Sprintf("%s:%d", ip4, c.Port) + if strings.Contains(ip, ":") { + // IPv6 address requires extra brackets in order to + // distinguish address from port + addr = fmt.Sprintf("[%s]:%d", ip, c.Port) + } else { + addr = fmt.Sprintf("%s:%d", ip, c.Port) + } } roller, err := utls.NewRoller() -- GitLab