master
1package get
2
3import (
4 "context"
5 "fmt"
6 "net"
7 "strings"
8
9 "github.com/carlmjohnson/requests"
10 "github.com/kahnwong/swissknife/configs/color"
11)
12
13type PublicIPResponse struct {
14 Ip string `json:"ip"`
15}
16
17type IPLocationResponse struct {
18 Ip string `json:"ip"`
19 Country string `json:"country"`
20 RegionName string `json:"regionName"`
21}
22
23func getInternalIP() (string, error) {
24 var internalIP string
25
26 ifaces, err := net.Interfaces()
27 if err != nil {
28 return "", fmt.Errorf("failed to get network interfaces: %w", err)
29 }
30
31 for _, iface := range ifaces {
32 if iface.Flags&net.FlagUp == 0 {
33 continue // interface down
34 }
35 if iface.Flags&net.FlagLoopback != 0 {
36 continue // skip loopback interface
37 }
38
39 addrs, err := iface.Addrs()
40 if err != nil {
41 return "", fmt.Errorf("failed to get interface addresses: %w", err)
42 }
43
44 for _, addr := range addrs {
45 var ip net.IP
46
47 switch v := addr.(type) {
48 case *net.IPNet:
49 ip = v.IP
50 case *net.IPAddr:
51 ip = v.IP
52 }
53
54 if ip == nil || ip.IsLoopback() {
55 continue
56 }
57 if ip.To4() != nil {
58 if internalIP == "" {
59 internalIP = ip.String()
60 }
61 }
62 }
63 }
64
65 return internalIP, nil
66}
67
68func getLocalIP() (string, error) {
69 conn, err := net.Dial("udp", "8.8.8.8:80")
70 if err != nil {
71 return "", fmt.Errorf("error on net.Dial: %w", err)
72 }
73 defer func(conn net.Conn) {
74 if cErr := conn.Close(); cErr != nil {
75 // This overwrites the return "err" with a wrapped error
76 err = fmt.Errorf("error closing connection: %w", cErr)
77 }
78 }(conn)
79 localAddr := conn.LocalAddr().(*net.UDPAddr)
80
81 // sanitize
82 parts := strings.Split(fmt.Sprintf("%v", localAddr), ":")
83
84 localIP := ""
85 if len(parts) > 0 {
86 localIP = parts[0]
87 } else {
88 return "", fmt.Errorf("invalid address format")
89 }
90
91 return localIP, nil
92}
93
94func getPublicIP() (PublicIPResponse, error) {
95 var response PublicIPResponse
96 err := requests.
97 URL("https://api.ipify.org?format=json").
98 ToJSON(&response).
99 Fetch(context.Background())
100
101 if err != nil {
102 return PublicIPResponse{}, fmt.Errorf("error getting public ip: %w", err)
103 }
104
105 return response, nil
106}
107
108func getIPLocation(ip string) (IPLocationResponse, error) {
109 var response IPLocationResponse
110 err := requests.
111 URL(fmt.Sprintf("http://ip-api.com/json/%s?fields=query,country,regionName", ip)).
112 ToJSON(&response).
113 Fetch(context.Background())
114
115 if err != nil {
116 return IPLocationResponse{}, fmt.Errorf("error getting ip location: %w", err)
117 }
118
119 return response, nil
120}
121
122func IP() error {
123 internalIP, err := getInternalIP()
124 if err != nil {
125 return err
126 }
127 fmt.Printf("%s: %s\n", color.Green("Internal IP"), internalIP)
128
129 localIP, err := getLocalIP()
130 if err != nil {
131 return err
132 }
133 fmt.Printf("%s: %s\n", color.Green("Local IP"), localIP)
134
135 publicIP, err := getPublicIP()
136 if err != nil {
137 return err
138 }
139
140 IPLocation, err := getIPLocation(publicIP.Ip)
141 if err != nil {
142 return err
143 }
144 fmt.Printf("%s: %s (%s, %s)\n", color.Green("Public IP"), publicIP.Ip, color.Blue(IPLocation.RegionName), color.Blue(IPLocation.Country))
145 return nil
146}