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
155 lines
3.2 KiB
Go
155 lines
3.2 KiB
Go
package proc
|
|
|
|
import (
|
|
"fmt"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/pranshuparmar/witr/pkg/model"
|
|
)
|
|
|
|
func ListOpenPorts() ([]model.OpenPort, error) {
|
|
out, err := exec.Command("netstat", "-ano").Output()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
lines := strings.Split(string(out), "\n")
|
|
var ports []model.OpenPort
|
|
seen := make(map[string]bool)
|
|
|
|
for _, line := range lines {
|
|
fields := strings.Fields(line)
|
|
// TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 888 (len 5)
|
|
// UDP 0.0.0.0:123 *:* 999 (len 4)
|
|
|
|
if len(fields) < 4 {
|
|
continue
|
|
}
|
|
|
|
proto := fields[0]
|
|
if proto != "TCP" && proto != "UDP" && proto != "TCPv6" && proto != "UDPv6" {
|
|
continue
|
|
}
|
|
|
|
var pidStr, state string
|
|
if len(fields) == 4 {
|
|
pidStr = fields[3]
|
|
state = "LISTEN"
|
|
} else if len(fields) >= 5 {
|
|
pidStr = fields[4]
|
|
state = fields[3]
|
|
if state == "LISTENING" {
|
|
state = "LISTEN"
|
|
}
|
|
}
|
|
|
|
pid, err := strconv.Atoi(pidStr)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
localAddr := fields[1]
|
|
lastColon := strings.LastIndex(localAddr, ":")
|
|
if lastColon == -1 {
|
|
continue
|
|
}
|
|
portStr := localAddr[lastColon+1:]
|
|
ip := localAddr[:lastColon]
|
|
if len(ip) > 2 && strings.HasPrefix(ip, "[") && strings.HasSuffix(ip, "]") {
|
|
ip = ip[1 : len(ip)-1]
|
|
}
|
|
|
|
port, err := strconv.Atoi(portStr)
|
|
if err == nil {
|
|
key := fmt.Sprintf("%d|%d|%s", pid, port, ip)
|
|
if !seen[key] {
|
|
ports = append(ports, model.OpenPort{
|
|
PID: pid,
|
|
Port: port,
|
|
Address: ip,
|
|
Protocol: proto,
|
|
State: state,
|
|
})
|
|
seen[key] = true
|
|
}
|
|
}
|
|
}
|
|
return ports, nil
|
|
}
|
|
|
|
// GetSocketsForPID returns every IP socket owned by a PID, including
|
|
// non-listening sockets, by parsing `netstat -ano`.
|
|
func GetSocketsForPID(pid int) []model.Socket {
|
|
out, err := exec.Command("netstat", "-ano").Output()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
lines := strings.Split(string(out), "\n")
|
|
var sockets []model.Socket
|
|
seen := make(map[string]bool)
|
|
|
|
pidStr := strconv.Itoa(pid)
|
|
|
|
for _, line := range lines {
|
|
fields := strings.Fields(line)
|
|
// TCP: Proto LocalAddr ForeignAddr State PID (5 fields)
|
|
// UDP: Proto LocalAddr *:* PID (4 fields)
|
|
if len(fields) < 4 {
|
|
continue
|
|
}
|
|
|
|
proto := strings.ToUpper(fields[0])
|
|
var matchPID, state string
|
|
if strings.HasPrefix(proto, "TCP") {
|
|
if len(fields) < 5 {
|
|
continue
|
|
}
|
|
state = fields[3]
|
|
if state == "LISTENING" {
|
|
state = "LISTEN"
|
|
}
|
|
matchPID = fields[4]
|
|
} else if strings.HasPrefix(proto, "UDP") {
|
|
state = "OPEN"
|
|
matchPID = fields[3]
|
|
} else {
|
|
continue
|
|
}
|
|
|
|
if matchPID != pidStr {
|
|
continue
|
|
}
|
|
|
|
localAddr := fields[1]
|
|
lastColon := strings.LastIndex(localAddr, ":")
|
|
if lastColon == -1 {
|
|
continue
|
|
}
|
|
portStr := localAddr[lastColon+1:]
|
|
ip := localAddr[:lastColon]
|
|
if len(ip) > 2 && strings.HasPrefix(ip, "[") && strings.HasSuffix(ip, "]") {
|
|
ip = ip[1 : len(ip)-1]
|
|
}
|
|
|
|
port, err := strconv.Atoi(portStr)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
key := proto + "|" + ip + "|" + portStr + "|" + state
|
|
if seen[key] {
|
|
continue
|
|
}
|
|
seen[key] = true
|
|
sockets = append(sockets, model.Socket{
|
|
Port: port,
|
|
Address: ip,
|
|
Protocol: proto,
|
|
State: state,
|
|
})
|
|
}
|
|
return sockets
|
|
}
|