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}