#!/bin/bash

: '
Author: netico <netico@riseup.net>

Thu 13 Jan 2022 05:43:29 PM CET
Mon 17 Jan 2022 11:33:08 AM CET
Fri 11 Feb 2022 10:26:56 AM CET

TCP investigation script
'

# CONFIGURATION
# Sender
SENDER="someone@domain.tld"
# Recipient
RECIPIENT="someone.else@domain.tld"
# Mail relay URL
RELAY="smtp://powerful.mail.server:587"


# ERRORS HANDLING
E=false
if [[ $EUID -ne 0 ]]
then
    E=true
    ERROR="This script must be run as root!"
fi

# FUNCTIONS
# See https://stackoverflow.com/questions/19059944/convert-kb-to-mb-using-bash
hprint() {
    local -i bytes=$1;
    if [[ $bytes -lt 1024 ]]
    then
        echo "${bytes}B"
    elif [[ $bytes -lt 1048576 ]]
    then
        echo "$(( (bytes + 1023)/1024 ))kB"
    else
        echo "$(( (bytes + 1048575)/1048576 ))MB"
    fi
}
pidinfo() {
    local PID="$1"
    local STAT=$(pidstat -p $PID -d | tail -1)
    local READ=$(echo $STAT | awk '{print $5}')
    local WRITE=$(echo $STAT | awk '{print $6}')
    local DELETE=$(echo $STAT | awk '{print $7}')
    local IODELAY=$(echo $STAT | awk '{print $8}')

    echo "<h5>IO</h5>"
    echo "<table>"
    echo "<tr>"
    echo "<th>READ/S</th><th>WRITE/S</th><th>DEL/S</th><th>IO DELAY</th>"
    echo "</tr>"
    echo "<tr>"
    echo "<td>${READ}kB</td><td>${WRITE}kB</td><td>${DELETE}KB</td><td>$IODELAY</td>"
    echo "</tr>"
    echo "</table>"

    echo "<h5>FILES</h5>"
    echo "<table>"
    echo "<tr>"
    echo "<th>TYPE</th><th>SIZE</th><th>NAME</th>"
    echo "</tr>"

    while read -r FIELD; do
        case ${FIELD::1} in
          s)
            local SIZE=$(echo -n ${FIELD:1})
            ;;
          t)
            local TYPE=$(echo -n ${FIELD:1})
            ;;
          n)
            local NAME=$(echo -n ${FIELD:1})
            ;;
        esac
        if [ -e "$NAME" ] && [ -n "$TYPE" ] && [ "$SIZE" -gt 0 ]
        then    
            echo "<tr><td>$TYPE</td><td>" $(hprint $SIZE) "</td><td>$NAME</td></tr>"
            SIZE=0
            TYPE=""
            NAME=""
        fi

    done < <(lsof -p $PID -w -F pstn)
    echo "</table>"    
}

# MAIN
# IP address
# IP="192.168.0.100"
IP=$(hostname -I | awk {'print $1'})
# HTML report
HTML="/tmp/report.html.$RANDOM"
echo "<!DOCTYPE html>" > $HTML
echo "<html>" >> $HTML
echo "<head>" >> $HTML
echo "<meta charset='UTF-8'>" >> $HTML
echo "<title>TCP investigation</title>" >> $HTML
echo "<style>" >> $HTML
echo "body {margin: 0pt; padding: 10pt;}" >> $HTML
echo "div, p {width: 100%; padding: 0pt; color: MidnightBlue; font-size: 11pt;}" >> $HTML
echo "table {width: 100%; font-size: 10pt; color: DarkSlateGrey; border: 1pt solid LightGrey; margin: 0pt 0pt 10pt 0pt; padding: 1.2pt; border-radius: 5pt;}" >> $HTML
echo "th, td {text-align: left; padding: 1.2pt}" >> $HTML
echo "a {text-decoration: none;}" >> $HTML
echo "h1, h2, h3, h4, h5 {text-align: left; margin: 0pt 0pt 10pt 0pt; padding: 0pt;}" >> $HTML
echo "</style>" >> $HTML
echo "</head>" >> $HTML
echo "<body>" >> $HTML
echo "<h1>TCP investigation</h1>" >> $HTML
echo "<p><b>IP</b>: $IP</p>" >> $HTML
echo "<p><b>Date</b>: "$(date)"</p>" >> $HTML
# Are there any active connections?
ITEMS=$(netstat -pant 2>/dev/null | grep "$IP" | grep ESTABLISHED | wc -l)
if [[ $ITEMS -eq 0 ]]
then
    E=true
    ERROR="There are no active connections"
fi
# Is everything okay?
if [ "$E" = true ]
then
    echo "<p><b>Error</b>: $ERROR</p>" >> $HTML
else
    # Active connections
    declare -a P
    declare -a C
    I=0
    echo "<h3 id='connections'>ACTIVE TCP CONNECTIONS</h3>"$'\n' >> $HTML
    echo "<table>" >> $HTML
    echo "<tr>" >> $HTML
    echo "<th>PID</th><th>REMOTE IP</th><th>REMOTE PORT</th><th>USER</th><th>GROUP</th><th>COMMAND</th>" >> $HTML
    echo "</tr>" >> $HTML
    while read -r CONNECTION; do
        if [[ $CONNECTION ]]
        then
            PID=$(echo $CONNECTION | awk '{print $7}' | cut -d/ -f1)
            if [[ "$PID" =~ ^[0-9]+$ ]]
	        then	        
  	            USER=$(ps -p $PID -o user,group=GROUP | tail -1 | awk '{print $1}')
  	            GROUP=$(ps -p $PID -o user,group=GROUP | tail -1 | awk '{print $2}')    
  	            COMMAND=$(ps -p $PID -o args=COMMAND | tail -1)
	            REMOTE=$(echo $CONNECTION | awk '{print $5}' | cut -d: -f1)
	            PORT=$(echo $CONNECTION | awk '{print $5}' | cut -d: -f2)
		        C[$I]="$PID|$REMOTE|$PORT|$USER|$GROUP|$COMMAND"
		        P[$I]=$PID
		        I=$(expr $I + 1)
	        fi
        fi
    done < <(netstat -pant 2>/dev/null | grep $IP | grep ESTABLISHED)
    readarray -t C < <(printf '%s\n' "${C[@]}" | sort -n | uniq)
    for L in "${C[@]}"
    do
        echo "<tr>" >> $HTML
        LPID=$(echo $L | cut -d'|' -f1)
        REMOTE=$(echo $L | cut -d'|' -f2)
        PORT=$(echo $L | cut -d'|' -f3)
        USER=$(echo $L | cut -d'|' -f4)
        GROUP=$(echo $L | cut -d'|' -f5)
        COMMAND=$(echo $L | cut -d'|' -f6)
        echo "<td><a href='#pid-"$LPID"'>$LPID</a></td><td>$REMOTE</td><td>$PORT</td><td>$USER</td><td>$GROUP</td><td>$COMMAND</td>" >> $HTML
        echo "</tr>" >> $HTML
    done
    echo "</table>" >> $HTML

    echo "<h3>PROCESSES INVOLVED</h3>" >> $HTML
    readarray -t P < <(printf '%s\n' "${P[@]}" | sort -n | uniq)
    for PID in "${P[@]}"
    do
        echo "<h4 id='pid-"$PID"'><a href='#connections'>&uarr;</a> PROCESS ID $PID</h4>" >> $HTML
        pidinfo $PID  >> $HTML
    done
fi
echo '</body>' >> $HTML
echo '</html>' >> $HTML

# REPORT
# Internet Message Format
MSG="/tmp/report.eml.$RANDOM"
# Message headers
FROM="From: \"TCP investigation\" <$SENDER>"
TO="To: \"Report\" <$RECIPIENT>"
SUBJECT="Subject: TCP investigation"
MIME="MIME-Version: 1.0"
TYPE="Content-Type: text/html; charset=UTF-8"
DATE="Date: $(date -R)"
ENCODING="Content-Transfer-Encoding: base64"
DISPOSITION="Content-Disposition: inline"
# Message-ID ?
# https://en.wikipedia.org/wiki/Message-ID
# https://www.jwz.org/doc/mid.html
# https://datatracker.ietf.org/doc/html/rfc2392
echo $FROM > $MSG
echo $TO >> $MSG
echo $SUBJECT >> $MSG
echo $DATE >> $MSG
echo $MIME >> $MSG
echo $TYPE >> $MSG
echo $ENCODING >> $MSG
echo $DISPOSITION >> $MSG
echo >> $MSG
# Encode HTML
echo $(base64 <<< "$(cat $HTML)") >> $MSG
# https://serverfault.com/questions/1036588/error-message-on-emailing-998-octets
sed -i -E 's/.{998}/&\n/g' $MSG
# https://cr.yp.to/docs/smtplf.html
unix2dos -q $MSG

# Sending e-mail
curl -s -n --ssl-reqd $RELAY --mail-from $SENDER --mail-rcpt $RECIPIENT --upload-file $MSG

# Cleaning
rm -f "$MSG"
rm -f "$HTML"
