-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathGetNetstatCSV.sh
More file actions
125 lines (101 loc) · 3.42 KB
/
GetNetstatCSV.sh
File metadata and controls
125 lines (101 loc) · 3.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/bin/sh
# Provides netstat information in a format expected by Squared Up's Visual Application Discovery and Analysis feature.
# Copyright 2018 Squared Up Limited, All Rights Reserved.
# Improved for performance: caches per-PID lookups; avoids extra processes - 02/24/2026
Format="$1"
if [ -z "$Format" ]; then
Format="csv"
fi
localHostName=${HOSTNAME%%.*}
processDescMaxLength=75
case "$Format" in
csv) lineEnd="\n" ;;
csvEx) lineEnd="%EOL%" ;;
*)
printf '%s\n' "Unknown format type $Format"
exit 1
;;
esac
# Decide whether we need sudo (and keep it as a string awk can use)
if [ "$(id -u)" -ne 0 ]; then
elevate="sudo"
else
elevate=""
fi
header="Computername,PID,ProcessName,ProcessDescription,Protocol,LocalAddress,LocalPort,RemoteAddress,RemotePort,State,RemoteAddressIP"
if [ "$lineEnd" = "\n" ]; then
printf '%s\n' "$header"
else
printf '%s' "$header$lineEnd"
fi
# Run netstat once, optionally via sudo
if [ -n "$elevate" ]; then
NETCMD="sudo netstat -tpn"
else
NETCMD="netstat -tpn"
fi
# AWK: filter ESTABLISHED in-process; cache per PID; avoid cut/grep
$NETCMD | head -n 400 2>/dev/null | awk \
-v ORS="$lineEnd" \
-v OFS=',' \
-v host="$localHostName" \
-v processDescMaxLength="$processDescMaxLength" \
-v elevate="$elevate" '
# Skip anything that does not look like a TCP line
$1 !~ /^tcp/ { next }
# Only ESTABLISHED (netstat state column is usually $6 for -tpn TCP lines)
$6 != "ESTABLISHED" { next }
{
# Local endpoint $4, remote endpoint $5
localEpSplit = match($4, ":[0-9]+$")
remoteEpSplit = match($5, ":[0-9]+$")
localAddr = (localEpSplit ? substr($4, 1, localEpSplit - 1) : $4)
localPort = (localEpSplit ? substr($4, localEpSplit + 1) : "")
remoteAddr = (remoteEpSplit ? substr($5, 1, remoteEpSplit - 1) : $5)
remotePort = (remoteEpSplit ? substr($5, remoteEpSplit + 1) : "")
# Extract PID from "PID/Program"
split($7, pidParts, "/")
pid = pidParts[1]
if (pid == "-" || pid == "") {
pid = -1
comm = "Unknown"
args = "\"\""
} else {
# Cache per PID to avoid running ps repeatedly
if (!(pid in commCache)) {
# Process name
if (elevate != "") cmd = elevate " ps -o comm= --pid " pid
else cmd = "ps -o comm= --pid " pid
cmd | getline comm
close(cmd)
if (comm == "") comm = "Unknown"
commCache[pid] = comm
# Full args (truncate in awk instead of cut)
if (elevate != "") cmd2 = elevate " ps -o args= --pid " pid
else cmd2 = "ps -o args= --pid " pid
cmd2 | getline argsRaw
close(cmd2)
if (argsRaw == "") argsRaw = ""
# Truncate
if (length(argsRaw) > processDescMaxLength) {
argsRaw = substr(argsRaw, 1, processDescMaxLength)
}
# Append "..." if exactly max length and not already ending in it
if (length(argsRaw) == processDescMaxLength) {
if (substr(argsRaw, length(argsRaw)-2, 3) != "...") {
argsRaw = argsRaw "..."
}
}
# CSV escape quotes and wrap
gsub(/"/, "\"\"", argsRaw)
argsCache[pid] = "\"" argsRaw "\""
}
comm = commCache[pid]
args = argsCache[pid]
}
if (localAddr != remoteAddr){
# Output row
print host, pid, comm, args, toupper($1), localAddr, localPort, remoteAddr, remotePort, $6, remoteAddr
}
}'
exit 0