/
external_ip.go
70 lines (60 loc) · 1.8 KB
/
external_ip.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package feed
import (
"context"
"fmt"
"io"
"log"
"net"
"net/http"
"strings"
"time"
)
var (
storedIP IPv4
publicIP = net.ParseIP(storedIP.read())
publicIPResolverURLs = []string{"http://ifconfig.io", "https://api.ipify.org"}
publicIPResolverURL = publicIPResolverURLs[1]
)
func onError(msg string, arg interface{}) string {
log.Println(msg, arg)
return ""
}
// PublicIP returns current public IP of the system as a string. Returns emptry string if not changed or could not be determined.
func PublicIP(ctx context.Context) (ipAddress string) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, publicIPResolverURL, nil)
if err != nil {
return onError("error getting public IP", err)
}
// ifconfig.io does not like programmatic access
req.Header.Add("User-Agent", "curl/7.74.0")
c := &http.Client{Timeout: time.Second * 30}
resp, err := c.Do(req)
if err != nil {
return onError("error getting public IP", err)
}
if resp.StatusCode == 200 {
if resp.ContentLength > int64(len("255.255.255.255\r")) {
return onError("error getting public IP: long response", resp.ContentLength)
}
defer resp.Body.Close()
body := make([]byte, 26)
rbytes, err := resp.Body.Read(body)
if err == nil || err == io.EOF {
if rbytes >= len("1.1.1.1\r") && rbytes <= len("255.255.255.255\r") {
newIP := net.ParseIP(strings.TrimSpace(string(body[0:rbytes])))
if newIP != nil {
if !newIP.Equal(publicIP) {
publicIP = newIP
storedIP = IPv4(publicIP.String())
storedIP.write()
return string(storedIP)
}
return ""
}
}
return onError("error parsing public IP:", string(body))
}
return onError("error getting public IP:", err)
}
return onError("error code getting public IP", fmt.Sprintf("%d %s", resp.StatusCode, resp.Status))
}