|
package main |
|
|
|
import ( |
|
"context" |
|
"crypto/tls" |
|
"fmt" |
|
"io" |
|
"log" |
|
"net" |
|
"net/http" |
|
"strings" |
|
"time" |
|
|
|
"github.com/miekg/dns" |
|
) |
|
|
|
func main() { |
|
client := &http.Client{ |
|
Timeout: time.Second * 5, |
|
Transport: &http.Transport{ |
|
// Avoid: "x509: certificate signed by unknown authority" |
|
TLSClientConfig: &tls.Config{ |
|
InsecureSkipVerify: true, |
|
}, |
|
DialContext: func(ctx context.Context, network string, addr string) (net.Conn, error) { |
|
ipv4, err := resolveIPv4(addr) |
|
if err != nil { |
|
return nil, err |
|
} |
|
timeout, err := time.ParseDuration("10s") |
|
if err != nil { |
|
return nil, err |
|
} |
|
return (&net.Dialer{ |
|
Timeout: timeout, |
|
}).DialContext(ctx, network, ipv4) |
|
}, |
|
}, |
|
} |
|
|
|
// Also try: https://v4.testmyipv6.com/ |
|
req, err := http.NewRequest("GET", "https://ipv4.lookup.test-ipv6.com/", nil) |
|
if err != nil { |
|
log.Fatal(err) |
|
} |
|
|
|
res, err := client.Do(req) |
|
if err != nil { |
|
log.Fatal(err) |
|
} |
|
|
|
b, err := io.ReadAll(res.Body) |
|
if err != nil { |
|
log.Fatal(err) |
|
} |
|
fmt.Printf("%+v\n", string(b)) |
|
} |
|
|
|
// resolveIPv4 resolves an address to IPv4 address. |
|
func resolveIPv4(addr string) (string, error) { |
|
url := strings.Split(addr, ":") |
|
|
|
m := new(dns.Msg) |
|
m.SetQuestion(dns.Fqdn(url[0]), dns.TypeA) |
|
m.RecursionDesired = true |
|
|
|
// NOTE: you shouldn't consult or rely on /etc/resolv.conf as it has proven historically to contain nameservers that don't respond. |
|
config, _ := dns.ClientConfigFromFile("/etc/resolv.conf") |
|
c := new(dns.Client) |
|
r, _, err := c.Exchange(m, net.JoinHostPort(config.Servers[0], config.Port)) |
|
if err != nil { |
|
return "", err |
|
} |
|
for _, ans := range r.Answer { |
|
if a, ok := ans.(*dns.A); ok { |
|
url[0] = a.A.String() |
|
} |
|
} |
|
|
|
return strings.Join(url, ":"), nil |
|
} |
I'm thinking on leave the control of all the DNS calls to Golang by providing that "Resolver.Dial" function, everytime the function is called the code get the next DNS address. This approach has two great things (at least for my use case):