# -*- tab-width: 4 -*- ;; Emacs
# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM
############################################################ IDENT(1)
#
# $Title: dwatch(8) gource module for network activity $
# $Copyright: 2014-2018 Devin Teske. All rights reserved. $
# $FrauBSD: dwatch-gource/gource-net 2018-05-29 21:47:06 +0000 freebsdfrau $
#
############################################################ DESCRIPTION
#
# Produce gource custom log format for network activity
#
############################################################ PROBE

_RAW_PROFILE=gource-net-raw
load_profile $_RAW_PROFILE

############################################################ GLOBALS

: ${_DEBUG=}

############################################################ MAIN

if [ "$DEBUG$EXIT_AFTER_COMPILE" ]; then
	eval dwatch $ARGV -qX $_RAW_PROFILE
	exit
fi

info "Watching '$PROBE' ..."
eval dwatch $ARGV -qX $_RAW_PROFILE | awk -v debug=$_DEBUG '
	################################################## BEGIN
	BEGIN {
		stdout = "/dev/stdout"
		stderr = "/dev/stderr"

		date = "[[:digit:]]+ [A-Z][a-z][a-z] [0-9 ][0-9]"
		time = "[0-9][0-9]:[0-9][0-9]:[0-9][0-9]"
		rcvd = "/input|#e62020"
		send = "/output|#32cd32"

		delete noptr
		delete addr2host
		addr2host["127.0.0.1"] = addr2host["::1"] = "localhost"

		delete port2service
		while (getline < "/etc/services") {
			if (/^[[:space:]]*(#|$)/ || !sub("/tcp.*", "", $2))
				continue
			port2service[$2] = $1
		}
	}
	################################################## FUNCTIONS
	function dprint(text) {
		if (!debug) return
		print "DEBUG: " text > stderr
		fflush(stderr)
	}
	function gource(user, type, path,        str) {
		str = sprintf("%u|%s|%s|%s", epoch, user, type, path)
		print str
		fflush(stdout)
		dprint(str)
	}
	function add(user, path) {
		gource(user, "A", path)
		gource(user, "A", path rcvd)
		gource(user, "A", path send)
	}
	function mod(user, path) {
		gource(user, path ~ "/udp/" ? "D" : "M", path)
	}
	function del(user, path) {
		gource(user, "D", path send)
		gource(user, "D", path rcvd)
		gource(user, "D", path)
	}
	function service(port,        name) {
		return (name = port2service[port]) ? name : port
	}
	function host(addr,        name, record, n, fields) {
		if (addr in addr2host) return addr2host[addr]
		if (addr in noptr) return addr
		(cmd = "host " addr) | getline record
		close(cmd)
		n = split(record, fields)
		if (fields[n-1] == "pointer") {
			sub(/\.$/, "", fields[n])
			return addr2host[addr] = fields[n]
		}
		noptr[addr] = 1
		return addr
	}
	################################################## MAIN
	debug { dprint($0) }
	length(datetime = substr($0, 1, 20)) != 20 { next }
	datetime !~ "^" date " " time "$" { next }
	(epoch = $7) ~ /^[[:digit:]]+/ {
		curproc = $6
		sub(/:$/, "", curproc)
		family = $8
		remote = $11
		if (!remote) next
		if (match(remote, /:[0-9]+$/)) remote = sprintf("%s/%s/%s",
			host(substr(remote, 1, RSTART-1)), family,
			service(substr(remote, RSTART+1)))
		else
			remote = host(remote)
	}
	$10 == "ACCEPT" || $10 == "CONNECT" { add(curproc, remote) }
	$10 == "RCVD" { mod(curproc, remote rcvd) }
	$10 == "SEND" { mod(curproc, remote send) }
	$10 == "CLOSE" && remote != "0.0.0.0:0" { del(curproc, remote) }
	################################################## END
' # END-QUOTE

exit $SUCCESS

################################################################################
# END
################################################################################
