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}