diff --git a/pkg/bootstrap/doh.go b/pkg/bootstrap/doh.go index d56bcf092c4a3275766a3e07983c089fe8ce2ae5..b96ec9a09eb09219d348cf5f56e5deace743fa16 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 4f01b589e4e16006abeb6e180cd668ed991d2773..1a7d83e726269cb7af449e329cb6728ac374d233 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 4347bca69e178442a632875baf0910c0394216b3..871231dbb0b6d7a611724dbfdf890058e4880403 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()