36b3af2e3d
PR Check / Code Quality: Format (push) Failing after 1s
PR Check / Code Quality: Lint (darwin) (push) Failing after 0s
PR Check / Code Quality: Lint (freebsd) (push) Failing after 1s
PR Check / Code Quality: Lint (windows) (push) Failing after 1s
PR Check / Code Quality: Lint (linux) (push) Failing after 1s
PR Check / Security: Vulnerability Scan (push) Failing after 0s
Update Documentation / update-docs (push) Failing after 2s
PR Check / Code Quality: Vendor (push) Failing after 1s
PR Check / Code Quality: Coverage (push) Failing after 0s
PR Check / Tests: Unit (macos-latest) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04-arm) (push) Has been cancelled
PR Check / Tests: Unit (windows-latest) (push) Has been cancelled
147 lines
3.0 KiB
Go
147 lines
3.0 KiB
Go
//go:build darwin
|
|
|
|
package proc
|
|
|
|
import (
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/pranshuparmar/witr/pkg/model"
|
|
)
|
|
|
|
func ListOpenPorts() ([]model.OpenPort, error) {
|
|
cmd := exec.Command("lsof", "-i", "-P", "-n")
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var ports []model.OpenPort
|
|
lines := strings.Split(string(out), "\n")
|
|
|
|
startIdx := 0
|
|
if len(lines) > 0 && strings.HasPrefix(lines[0], "COMMAND") {
|
|
startIdx = 1
|
|
}
|
|
|
|
for _, line := range lines[startIdx:] {
|
|
fields := strings.Fields(line)
|
|
if len(fields) < 9 {
|
|
continue
|
|
}
|
|
|
|
pidStr := fields[1]
|
|
pid, err := strconv.Atoi(pidStr)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
protocol := fields[7]
|
|
if protocol != "TCP" && protocol != "UDP" {
|
|
if strings.Contains(line, "TCP") {
|
|
protocol = "TCP"
|
|
} else if strings.Contains(line, "UDP") {
|
|
protocol = "UDP"
|
|
} else {
|
|
protocol = "UNKNOWN"
|
|
}
|
|
}
|
|
|
|
nameField := fields[8] // Address:Port
|
|
state := "UNKNOWN"
|
|
if len(fields) > 9 {
|
|
state = strings.Trim(fields[9], "()")
|
|
} else if protocol == "UDP" {
|
|
state = "OPEN"
|
|
}
|
|
|
|
addr, port := parseNetstatAddr(nameField)
|
|
if port == 0 {
|
|
lastColon := strings.LastIndex(nameField, ":")
|
|
if lastColon != -1 {
|
|
portStr := nameField[lastColon+1:]
|
|
if p, err := strconv.Atoi(portStr); err == nil {
|
|
port = p
|
|
addr = nameField[:lastColon]
|
|
if addr == "*" {
|
|
addr = "0.0.0.0"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if port > 0 {
|
|
ports = append(ports, model.OpenPort{
|
|
PID: pid,
|
|
Port: port,
|
|
Address: addr,
|
|
Protocol: protocol,
|
|
State: state,
|
|
})
|
|
}
|
|
}
|
|
|
|
return ports, nil
|
|
}
|
|
|
|
// parseNetstatAddr parses addresses like "*.8080", "127.0.0.1.8080", "[::1].8080"
|
|
func parseNetstatAddr(addr string) (string, int) {
|
|
// Handle IPv6 format [::]:port or [::1]:port
|
|
if strings.HasPrefix(addr, "[") {
|
|
// IPv6 format
|
|
bracketEnd := strings.LastIndex(addr, "]")
|
|
if bracketEnd == -1 {
|
|
return "", 0
|
|
}
|
|
ip := addr[1:bracketEnd]
|
|
rest := addr[bracketEnd+1:]
|
|
// rest should be ":port" or ".port"
|
|
if len(rest) > 1 && (rest[0] == ':' || rest[0] == '.') {
|
|
port, err := strconv.Atoi(rest[1:])
|
|
if err == nil {
|
|
if ip == "::" || ip == "" {
|
|
return "::", port
|
|
}
|
|
return ip, port
|
|
}
|
|
}
|
|
return "", 0
|
|
}
|
|
|
|
// Handle formats like "*:8080" or "*.8080"
|
|
if strings.HasPrefix(addr, "*") {
|
|
if len(addr) > 1 && (addr[1] == ':' || addr[1] == '.') {
|
|
port, err := strconv.Atoi(addr[2:])
|
|
if err == nil {
|
|
return "0.0.0.0", port
|
|
}
|
|
}
|
|
return "", 0
|
|
}
|
|
|
|
// Handle IPv4 format: "127.0.0.1:8080" or "127.0.0.1.8080"
|
|
// Try colon-separated first (standard format)
|
|
if idx := strings.LastIndex(addr, ":"); idx != -1 {
|
|
ip := addr[:idx]
|
|
portStr := addr[idx+1:]
|
|
port, err := strconv.Atoi(portStr)
|
|
if err == nil {
|
|
return ip, port
|
|
}
|
|
}
|
|
|
|
// macOS netstat uses dot-separated: "127.0.0.1.8080"
|
|
// Find the last dot and check if what follows is a port
|
|
if idx := strings.LastIndex(addr, "."); idx != -1 {
|
|
portStr := addr[idx+1:]
|
|
port, err := strconv.Atoi(portStr)
|
|
if err == nil {
|
|
ip := addr[:idx]
|
|
return ip, port
|
|
}
|
|
}
|
|
|
|
return "", 0
|
|
}
|