#!/bin/bash
if [[ "$1" == 'debug'  ]]; then set -x;		_DEBUG=$1; shift; fi
if [[ "$1" == 'debug2' ]]; then set -xvT;	_DEBUG=$1; shift; fi
if [[ "$1" == 'trace'  ]]; then 		_TRACE=$1; shift; fi
####################################################################
# - WatchLG -
# Watch for login break-in attempts 
# Record in database ... and inject into firewall
# after maximum 'affairs' was reached
#-------------------------------------------------
REALPATH=`realpath $0`
WHERE=`dirname $REALPATH`
ME=`basename $REALPATH`
cd $WHERE
#-------- Core stuff -----
. ../../system.conf
. ../../watchermap.conf	# needed for debugging
. ../../common.conf
. ../../common.bashlib
#-------- API stuff -------
. ../../api/bash/sql.bashlib
#-------- Private stuff ---
. $ME.conf
. private.bashlib

: echo $PATH

THIS_IP=`dig +short $HOSTNAME`		# BASH knows HOSTNAME

trap refresh		HUP		# React on a 'HUP' for the filter refresh
trap stopservice        TERM INT	# React on a 'TERM' or CTL-C when debugging
trap rerun              KILL 9		# React on crash 
trap leave		TRAP		# Leave program on serious errors
trap 'errorexit $? $_'	EXIT

#########################################
# Trap functions ...
#########################################
refresh() {
local funtag="[${FUNCNAME[0]}]"

	trace	"$funtag Reloading filters for $ME ..."
	logger  "$ME[$$]: $funtag Reloading filters for $ME ..."
	load_filter "$funtag `date --iso=seconds`" $COMPRESS_FILTER
}

errorexit() {
local funtag="[${FUNCNAME[0]}]"

	logger	"$ME[$$]: $funtag trapped with exit code '$1', Cmd: '$2' ... Loop: $LOOP"
	trace	"$funtag $ME trapped with exit code '$1', Cmd: '$2' ... Loop: $LOOP"
	trace	"$funtag BASH Source: $BASH_SOURCE"
	trace	"$funtag Caller     : $(caller)"
	trace	"$funtag BASH line 0: ${BASH_LINENO[0]}"
	trace	"$funtag BASH line 1: ${BASH_LINENO[1]}"
	trace	"$funtag BASH line 2: ${BASH_LINENO[2]}"
	exit	255
}

stopservice() {
local funtag="[${FUNCNAME[0]}]"

        logger	"$ME[$$]: $funtag Terminating on request ..."
        trace	"$funtag Terminating on request ..."
        exit
}

leave() {
local funtag="[${FUNCNAME[0]}]"
local	rc=$?
local	lastcmd="$BASH_COMMAND"
local	lineno=${BASH_LINENO[0]}

	trace "$funtag $ME leaving at line $lineno: '$lastcmd' (exit code: $rc)"
	logger "$ME[$$]: $funtag leaving at line $lineno: '$lastcmd' (exit code: $rc)"
	exit 254
}

rerun() {
local funtag="[${FUNCNAME[0]}]"
local	token=$(pick_token $ME)

	log     "$ME: $funtag Abnormal termination, exit status: $?, Loop; $LOOP"
	logger  "$ME[$$]: Restarting in a minute ..."
	trace	"$funtag $ME Restarting in a minute ..."
	echo	"Restart $token" | at "now + 1 minute"
        exit
}

load_filter() {
# $1 .. filter status
# $2 .. compress flag; i.e. compress it not empty
local funtag="[${FUNCNAME[0]}]"

	unset -f filter
	[[ -f filter	   	]]  && mv filter filter.old
	[[ -f filter.compress	]]  && rm -f filter.compress

	./mkfilter $1

	if [ -f filter.old ]
	then	diff -c filter filter.old > filter-diffs
	else	[[ -f filter-diffs ]] && rm -f filter-diffs
	fi

	# If the 'compress filter' flag is set compress filter; 
	# i.e. remove comments and empty lines for runtime
	if [ ! -z "$2" ]
	then	trace "$funtag Using filter compressed"
		compress_filter
		. filter.compress
	else	trace "$funtag Using filter plain"
		. filter
	fi
	
	# Create/refresh superfluous_map as well
	SUPERFLUOUS=`mk_superfluous`
}


# ----------------------- Main program ---------------------------
dump_runtime
config_table $DB

ln -sf $WHERE/LGinjector $TOOLS_LINK

trace	"--- $ME started on $HOSTNAME ... $SYSTEM $SYSVERS"
trace	"Watcher: $PRODUCT, $REVISION $1"

if [ -z "$TIME_SLICE" ]; then TIME_SLICE=60; fi

touch $LOG
chmod 600 $LOG
touch $ME.trace
chmod 600 $ME.trace

if [ ! -p $PIPE ]; then mkfifo -m 600 $PIPE; fi

#
# Load generated 'filter' function
#
load_filter initial $COMPRESS_FILTER

#
# Start scanning ...
#
logger	"$ME[$$]: Started listening on pipe $PIPE using dynamic filters"
log	"Started listening on pipe $PIPE using dynamic filters"
trace	"Started listening on pipe $PIPE using dynamic filters"

renice -10 $$
trace "Starting scanner ..."
while : # Only a crash can stop us 
do

	[ -e $DB-new ] && pickDB-new

	# Set start conditions for a scanner loop
	CLASS=''
	read < $PIPE
	LOOP_START=`date +%s%3N`
	read DYN_ADDR < $LOG_DIR/DYN_IP
	((LOOP++))	# Count loops
	: echo "--------[ Loop: $LOOP ]----------"

	if [ -z "$REPLY" ]
	then	: echo "Eeek! Empty line ..."
		continue
	fi	

	STAGE="[prepro]"
	# ------ Pre-Processor (Internal Rule set) --------------
	# Log lines w/o IP make no sense
	# » Must come FIRST of all «
	RULE=IPpresent
		BANDIT=`get_bandit`
		if [ -z "$BANDIT" ]
		then	: trace "[No IP addr] ... skipped"
			continue
		fi
	#
	# Kick off any junk ...
	#
	RULE=Superfluous
	#	SUPER=`grep -oE "$SUPERFLUOUS" <<< $REPLY`
	#	if [ ! -z "$SUPER" ]
		BASH_REMATCH=()
		[[ "$REPLY" =~ ($SUPERFLUOUS) ]]
		if [ -n "${BASH_REMATCH[0]}" ]
		then	: echo "Superfluous stuff ... ignored ..."
			continue
		fi

	RULE=Freehost
		if [[ "$REPLY" =~ "@Freehost:" ]]
		then
			DEL_ADDR=`get_bandit`
			: trace "$ME/$RULE Deleting $DEL_ADDR from $TABLE"
			freehost $DEL_ADDR
			continue
		fi

       	RULE=Self
		if [[ "$REPLY" =~ "$THIS_IP" ]]
		then    : trace "$ME/$RULE Oops ... me $THIS_IP"
			continue
		fi
		
       	RULE=DynIP
		if [[ "$REPLY" =~ "$DYN_ADDR" ]]
		then    : trace "$ME/$RULE Oops ... my dyn IP $DYN_ADDR"
			continue
		fi

	# Careful here $TRUSTED is a list of IP addresses
       	RULE=Trusted
		if [[ "$REPLY" =~ ($TRUSTLIST) ]]
		then	: trace "$ME/$RULE Oops ... ${BASH_REMATCH[0]} in trusted hosts ... skipping"
			continue
		fi

	#--------------------------------------	

	STAGE="[filter]"
	dump_loadrate
	trace	"[Loop: $LOOP] '$REPLY'"
	trace	"Debuging: Debug: '$_DEBUG', Trace: '$_TRACE'"
	
	FILT_START=`date +%s%3N`
	filter 		# Returns with $BANDIT, $CLASS, $TAG, $ACTION and $RULE set
	FILT_RESULT=$?	# Keep retcode save for the post processor

	FILT_END=`date +%s%3N`
	FILT_TIME=$(( FILT_END-FILT_START ))
	LOOP_TIME=$(( FILT_END-LOOP_START ))

	STAGE="[postpro]"
	#
	# Post processor
	#
	case $FILT_RESULT in 
		0)	trace "[postpro] $RULE ... fall through, $LOOP_TIME/$FILT_TIME ms"
		;;
		1)	trace "[postpro] Finished for rule [$RULE], $LOOP_TIME/$FILT_TIME ms"
			continue
		;;	
		2)	trace "[postpro] Handled outside of $ME"
			continue
		;;
		3)	trace "[postpro] $BANDIT whitelisted ... nothing to do ..."
			continue
		#====== Return codes 240 ... 255 for internal use only ===========================
		;;
		240)	trace "[postpro] Legal login [$RULE] '$Pattern', $LOOP_TIME/$FILT_TIME ms"
			continue
		;;
		241)	trace "[postpro] Debuging set '$Pattern'"
			continue
		;;
		254)	trace "[postpro] Ignored ... [$RULE] '$Pattern', $LOOP_TIME/$FILT_TIME ms"
			continue
		;;
		255)	trace "[postpro] !!! Nasty things happend by rule '$RULE' for '$BANDIT'!!!"
		;;
		*)	trace "[postpro] Illegal return code '$FILT_RESULT' ... fall through"
	;;
	esac

	#-------------[ Catch all before loop finishes ]-----------
	RULE=UNTREATED
		log	"[$RULE] '$REPLY'"
		trace	"[$RULE] '$REPLY'"
done

# DEAD-End ... Reaching here triggers a trap that restarts us 
rerun
