Skip to content

Commit

Permalink
Merge pull request #771 from wakatime/bugfix/dns
Browse files Browse the repository at this point in the history
Use host ip address when dns fails
  • Loading branch information
gandarez authored Sep 8, 2022
2 parents 733a511 + 372b4d0 commit eb33ca0
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 31 deletions.
52 changes: 45 additions & 7 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@ package api

import (
"errors"
"fmt"
"net"
"net/http"
"strings"

"github.com/wakatime/wakatime-cli/pkg/log"
)

const (
// BaseURL is the base url of the wakatime api.
BaseURL = "https://api.wakatime.com/api/v1"
// baseIPAddrv4 is the base ip address v4 of the wakatime api.
baseIPAddrv4 = "143.244.210.202"
// baseIPAddrv6 is the base ip address v6 of the wakatime api.
baseIPAddrv6 = "2604:a880:4:1d0::2a7:b000"
// DefaultTimeoutSecs is the default timeout used for requests to the wakatime api.
DefaultTimeoutSecs = 120
)
Expand Down Expand Up @@ -59,19 +65,51 @@ func NewClient(baseURL string, opts ...Option) *Client {
func (c *Client) Do(req *http.Request) (*http.Response, error) {
resp, err := c.doFunc(c, req)
if err != nil {
// don't set alternate host if there's a custom api url
if !strings.HasPrefix(c.baseURL, BaseURL) {
return nil, err
}

var dnsError *net.DNSError
if errors.As(err, &dnsError) {
log.Warnf("dns error: %s. Retrying with fallback dns resolver", req.URL)
if !errors.As(err, &dnsError) {
return nil, err
}

t, err := NewTransportWithHostVerificationDisabled()
if err != nil {
return nil, err
}

c.client = &http.Client{
Transport: t,
}

c.client = &http.Client{
Transport: NewTransportWithCloudfareDNS(),
}
var alternateHost = baseIPAddrv4

return c.doFunc(c, req)
if isLocalIPv6() {
alternateHost = baseIPAddrv6
}

return nil, err
req.URL.Host = alternateHost

log.Warnf("dns error, it will retry with host ip address '%s'", alternateHost)

return c.doFunc(c, req)
}

return resp, nil
}

func isLocalIPv6() bool {
conn, err := net.Dial("udp", fmt.Sprintf("%s:80", baseIPAddrv4))
if err != nil {
log.Warnf("failed dialing to detect default local ip address: %s", err)
return true
}

defer conn.Close()

localAddr := conn.LocalAddr().(*net.UDPAddr)

return localAddr.IP.To4() == nil
}
34 changes: 10 additions & 24 deletions pkg/api/transport.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package api

import (
"context"
"crypto/tls"
"crypto/x509"
"net"
"net/http"
"strings"
"time"

"github.com/wakatime/wakatime-cli/pkg/log"
)

const serverName = "api.wakatime.com"

// NewTransport initializes a new http.Transport.
func NewTransport() *http.Transport {
return &http.Transport{
Expand All @@ -23,31 +23,17 @@ func NewTransport() *http.Transport {
}
}

// NewTransportWithCloudfareDNS initializes a new http.Transport with cloudfare DNS resolver.
func NewTransportWithCloudfareDNS() *http.Transport {
// NewTransportWithHostVerificationDisabled initializes a new http.Transport with disabled host verification.
func NewTransportWithHostVerificationDisabled() (*http.Transport, error) {
t := NewTransport()

t.DialTLSContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
r := &net.Resolver{
PreferGo: false,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
d := net.Dialer{
Timeout: DefaultTimeoutSecs * time.Second,
}

// ipv6 only
if strings.HasSuffix(network, "6") {
return d.DialContext(ctx, network, "[2606:4700:4700::1111]:53")
}

return d.DialContext(ctx, network, "1.1.1.1:53")
},
}

return r.Dial(ctx, network, addr)
t.TLSClientConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
RootCAs: CACerts(),
ServerName: serverName,
}

return t
return t, nil
}

// LazyCreateNewTransport uses the client's Transport if exists, or creates a new one.
Expand Down

0 comments on commit eb33ca0

Please sign in to comment.