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