zutils/cmd/network.go

232 lines
5.3 KiB
Go
Raw Permalink Normal View History

package cmd
import (
"fmt"
"net"
"os/exec"
"runtime"
"strings"
"github.com/zemenawi/zutils/pkg/colors"
)
func NetworkInfoCommand(args []string) error {
PrintBoxHeader("NETWORK INFORMATION", colors.Cyan)
PrintSectionHeader("IP ADDRESSES")
interfaces, err := net.Interfaces()
if err == nil {
for _, iface := range interfaces {
if iface.Flags&net.FlagUp != 0 && iface.Flags&net.FlagLoopback == 0 {
addrs, err := iface.Addrs()
if err == nil {
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
if ip != nil && !ip.IsLoopback() {
fmt.Printf("%s%s🌐%s %s%-18s%s %s\n",
colors.Green, colors.Bold, colors.Reset,
colors.Cyan, iface.Name, colors.Reset,
ip.String())
}
}
}
}
}
} else {
fmt.Printf("%sError getting network interfaces: %v%s\n", colors.Red, err, colors.Reset)
}
fmt.Println()
PrintSectionHeader("PUBLIC IP")
publicIP := getPublicIP()
if publicIP != "" {
fmt.Printf("%s%s🌍%s Public IP: %s%s%s\n",
colors.Green, colors.Bold, colors.Reset,
colors.Cyan, publicIP, colors.Reset)
} else {
fmt.Printf("%sUnable to determine public IP%s\n", colors.Yellow, colors.Reset)
}
fmt.Println()
PrintSectionHeader("DNS INFO")
dnsServers := getDNSServers()
for i, dns := range dnsServers {
fmt.Printf("%s%s🔗%s DNS Server %d: %s%s%s\n",
colors.Green, colors.Bold, colors.Reset,
i+1,
colors.Cyan, dns, colors.Reset)
}
fmt.Println()
PrintSectionHeader("ACTIVE CONNECTIONS")
connections := getNetworkConnections()
if len(connections) > 0 {
maxShow := 10
if len(connections) < maxShow {
maxShow = len(connections)
}
headers := []string{"#", "Proto", "State", "Local Address", "Remote Address"}
data := make([][]string, 0, maxShow)
for i := 0; i < maxShow; i++ {
parsed := parseConnection(connections[i])
row := []string{
fmt.Sprintf("%d", i+1),
parsed.proto,
parsed.state,
parsed.local,
parsed.remote,
}
data = append(data, row)
}
printSimpleTable(headers, data)
if len(connections) > maxShow {
fmt.Printf("%s%s...and %d more connections%s\n",
colors.Gray, colors.Bold, len(connections)-maxShow, colors.Reset)
}
fmt.Println()
} else {
fmt.Printf("%sNo active connections detected%s\n", colors.Yellow, colors.Reset)
fmt.Println()
}
return nil
}
func getPublicIP() string {
var cmd *exec.Cmd
if runtime.GOOS == "windows" {
cmd = exec.Command("curl", "-s", "ifconfig.me")
} else {
cmd = exec.Command("curl", "-s", "ifconfig.me")
}
output, err := cmd.CombinedOutput()
if err == nil {
return strings.TrimSpace(string(output))
}
cmd = exec.Command("wget", "-qO-", "ifconfig.me")
output, err = cmd.CombinedOutput()
if err == nil {
return strings.TrimSpace(string(output))
}
return ""
}
func getDNSServers() []string {
var dnsServers []string
if runtime.GOOS == "linux" {
cmd := exec.Command("sh", "-c", "grep '^nameserver' /etc/resolv.conf | awk '{print $2}'")
output, err := cmd.CombinedOutput()
if err == nil {
lines := strings.Split(strings.TrimSpace(string(output)), "\n")
for _, line := range lines {
if line != "" {
dnsServers = append(dnsServers, line)
}
}
}
} else if runtime.GOOS == "darwin" {
cmd := exec.Command("scutil", "--dns")
output, err := cmd.CombinedOutput()
if err == nil {
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if strings.Contains(line, "nameserver[") {
parts := strings.Fields(line)
if len(parts) >= 3 {
dnsServers = append(dnsServers, parts[2])
}
}
}
}
}
if len(dnsServers) == 0 {
dnsServers = append(dnsServers, "8.8.8.8 (Google DNS)")
dnsServers = append(dnsServers, "1.1.1.1 (Cloudflare DNS)")
}
return dnsServers
}
func getNetworkConnections() []string {
var connections []string
if runtime.GOOS == "linux" {
cmd := exec.Command("ss", "-tun")
output, err := cmd.CombinedOutput()
if err == nil {
lines := strings.Split(string(output), "\n")
for i := 1; i < len(lines) && i < 12; i++ {
line := strings.TrimSpace(lines[i])
if line != "" {
connections = append(connections, line)
}
}
}
} else if runtime.GOOS == "darwin" {
cmd := exec.Command("netstat", "-an")
output, err := cmd.CombinedOutput()
if err == nil {
lines := strings.Split(string(output), "\n")
for i := 0; i < len(lines) && i < 11; i++ {
line := strings.TrimSpace(lines[i])
if strings.Contains(line, "ESTABLISHED") {
connections = append(connections, line)
}
}
}
}
return connections
}
type connectionInfo struct {
proto string
state string
local string
remote string
}
func parseConnection(conn string) connectionInfo {
parts := strings.Fields(conn)
result := connectionInfo{}
if len(parts) >= 5 {
result.proto = parts[0]
result.state = parts[1]
// ss output format: proto state recv-q send-q local remote
// Try to find addresses by position
for i := 4; i < len(parts); i++ {
part := parts[i]
// Look for IP:port patterns
if strings.Contains(part, ":") && (strings.Contains(part, ".") || strings.Contains(part, ":")) {
if result.local == "" {
result.local = part
} else {
result.remote = part
}
}
}
}
return result
}