diff --git a/cmd/agent/monitor/myip.go b/cmd/agent/monitor/myip.go index c46f376..1db17d4 100644 --- a/cmd/agent/monitor/myip.go +++ b/cmd/agent/monitor/myip.go @@ -1,11 +1,17 @@ package monitor import ( + "context" "encoding/json" + "errors" "fmt" "io/ioutil" + "net" "net/http" + "strings" "time" + + "github.com/miekg/dns" ) type geoIP struct { @@ -13,22 +19,24 @@ type geoIP struct { IP string `json:"ip,omitempty"` } -var ipv4Servers = []string{ - "https://api-ipv4.ip.sb/geoip", - "https://ip4.seeip.org/geoip", -} - -var ipv6Servers = []string{ - "https://ip6.seeip.org/geoip", - "https://api-ipv6.ip.sb/geoip", -} - -var cachedIP, cachedCountry string +var ( + ipv4Servers = []string{ + "https://api-ipv4.ip.sb/geoip", + "https://ip4.seeip.org/geoip", + } + ipv6Servers = []string{ + "https://ip6.seeip.org/geoip", + "https://api-ipv6.ip.sb/geoip", + } + cachedIP, cachedCountry string + httpClientV4 = newHTTPClient(time.Second*20, time.Second*5, time.Second*10, false) + httpClientV6 = newHTTPClient(time.Second*20, time.Second*5, time.Second*10, true) +) func UpdateIP() { for { - ipv4 := fetchGeoIP(ipv4Servers) - ipv6 := fetchGeoIP(ipv6Servers) + ipv4 := fetchGeoIP(ipv4Servers, false) + ipv6 := fetchGeoIP(ipv6Servers, true) cachedIP = fmt.Sprintf("ip(v4:%s,v6:%s)", ipv4.IP, ipv6.IP) if ipv4.CountryCode != "" { cachedCountry = ipv4.CountryCode @@ -39,10 +47,16 @@ func UpdateIP() { } } -func fetchGeoIP(servers []string) geoIP { +func fetchGeoIP(servers []string, isV6 bool) geoIP { var ip geoIP + var resp *http.Response + var err error for i := 0; i < len(servers); i++ { - resp, err := http.Get(servers[i]) + if isV6 { + resp, err = httpClientV6.Get(servers[i]) + } else { + resp, err = httpClientV4.Get(servers[i]) + } if err == nil { body, err := ioutil.ReadAll(resp.Body) if err != nil { @@ -58,3 +72,70 @@ func fetchGeoIP(servers []string) geoIP { } return ip } + +func newHTTPClient(httpTimeout, dialTimeout, keepAliveTimeout time.Duration, ipv6 bool) *http.Client { + dialer := &net.Dialer{ + Timeout: dialTimeout, + KeepAlive: keepAliveTimeout, + } + + transport := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + ForceAttemptHTTP2: false, + DialContext: func(ctx context.Context, network string, addr string) (net.Conn, error) { + ip, err := resolveIP(addr, ipv6) + if err != nil { + return nil, err + } + return dialer.DialContext(ctx, network, ip) + }, + } + + return &http.Client{ + Transport: transport, + Timeout: httpTimeout, + } +} + +func resolveIP(addr string, ipv6 bool) (string, error) { + url := strings.Split(addr, ":") + + m := new(dns.Msg) + if ipv6 { + m.SetQuestion(dns.Fqdn(url[0]), dns.TypeAAAA) + } else { + m.SetQuestion(dns.Fqdn(url[0]), dns.TypeA) + } + m.RecursionDesired = true + + c := new(dns.Client) + r, _, err := c.Exchange(m, net.JoinHostPort("1.1.1.1", "53")) + if err != nil { + return "", err + } + + var ipv4Resolved, ipv6Resolved bool + for _, ans := range r.Answer { + if ipv6 { + if aaaa, ok := ans.(*dns.AAAA); ok { + url[0] = aaaa.AAAA.String() + ipv6Resolved = true + } + } else { + if a, ok := ans.(*dns.A); ok { + url[0] = a.A.String() + ipv4Resolved = true + } + } + } + + if ipv6 && !ipv6Resolved { + return "", errors.New("the AAAA record not resolved") + } + + if !ipv6 && !ipv4Resolved { + return "", errors.New("the A record not resolved") + } + + return strings.Join(url, ":"), nil +} diff --git a/go.mod b/go.mod index 07d9896..d98d4d2 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/golang/protobuf v1.4.2 github.com/google/go-github v17.0.0+incompatible github.com/gorilla/websocket v1.4.2 + github.com/miekg/dns v1.0.14 github.com/onsi/ginkgo v1.7.0 // indirect github.com/onsi/gomega v1.4.3 // indirect github.com/p14yground/go-github-selfupdate v0.0.0-20210520015421-eddf14461293 diff --git a/go.sum b/go.sum index 69238c1..6be33b8 100644 --- a/go.sum +++ b/go.sum @@ -228,6 +228,7 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ= github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -420,6 +421,7 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=