#===========================================================
# - common.bashlib -
# Function library for all dynloaders, modules & programs
#
# Common procedures 
# shared by all programs, dynloaders & modules
#===========================================================
#=======	... Do NOT change anything below ...	====
#===========================================================

setdebug() {
# $1	debug type
local   funtag="[${FUNCNAME[0]}]"

#	trace "$funtag '$1'"
	case $1 in 
		@nodebug)	declare -g	_DEBUG=''	;;
		@debug)		declare -g	_DEBUG='debug'	;;
		@debug2)	declare -g	_DEBUG='debug2'	;;
		@notrace)	declare -g	_TRACE=''	;;
		@trace)		declare -g	_TRACE='trace'	;;
		*)		declare -g	_DEBUG=''
				declare -g	_TRACE=''
	;;
	esac

	# Set DEBUG modes accordingly
	case $_DEBUG in
		debug)	set -x		;;
		debug2)	set -xvT	;;
		*)	set -
	;;	
	esac	

	trace "$funtag Debug: '$_DEBUG', Trace: '$_TRACE'"
}	

#
# log simple
#
log() {
        printf "%-18s %-s[%d]: %s\n" "`date +%Y-%m-%dT%T.%3N`" "$ME" "$$" "$*" >> $LOG
}

#
# Columized log 
# 4 columns: 10,10,15,rest
#
clog() {
        printf "%-18s %-s[%d]: %-10s %-10s %-15s %s\n" "`date +%Y-%m-%dT%T.%3N`" "$ME" "$$" "$1" "$2" "$3" "$4" >> $LOG
}

# Pass to GeoTrack pseudo-module
do_geotrack() {
local   funtag="[${FUNCNAME[0]}]"
local	callfunc=$3

	if [ -p $FIFO_BASE/GeoTrack ]
	then echo $BANDIT $ME $callfunc >> $FIFO_BASE/GeoTrack
	fi
}

trace() {
	if [ ! -z "$TRACE" ]
	then echo "`date +%Y-%m-%dT%T.%3N` $ME[$$]: $*" >> $ME.trace
	fi
}

# Check whether forward and reverse DNS resolution resolves to
# the same IP address
get_class() {
# $1 ..	an IP address
local thisIP
local thisHOST

        thisHOST=`dig +short -x $1`
        if [ -z "$thisHOST" ]; then echo NXDOMAIN; return; fi

        thisIP=`dig +short $thisHOST`
	if [[ "`echo $thisIP`" =~ "$1" ]]
        then    echo TRUEHOST
        else    echo FAKEHOST
        fi
}

validate_IP() {
	if [[ "$SYSSTYLE" =~ "rhel" ]]
	then	/bin/ipcalc -sc $1; return $?
	else	# Debian-ish do it the hard way ...
		$IPSET -q -exist add validate $1; return $?
	fi
}


# Pick the bandit's IP from the log line
# Probably enquoted by ticks (single quotes), parens,
# brackets or curly braces or even at the beginnig or
# end of the line e.g. for PostFix
#
# Common/universal ...
# Enclose the 'REPLY' in spaces helps matching
# not to fail with IP adresses at begining or end
# of the log line
get_bandit() {
	awk '
	BEGIN { rc=1 
		# Devel. note: See /root/bin/Test/IPmatch
		IP4PAT="([1-9]{1}[0-9]{0,2}[.]{1}){1}((0{1}[.])|([1-9]{1}[0-9]{0,2}[.])){2}((0{1})|([1-9]{1}[0-9]{0,2})){1}([\\] ]|[\\} ]|[\\) ]|[\\047 ]|[\\042 ]|[ ])+"
	}
	match($0,IP4PAT) {
				trail=substr($0,RSTART,RLENGTH)
				gsub(/[\[\]\{\}\(\)\047\042 ]/,"",trail)
				print trail
				rc=0
	}
	END { exit rc }
	' <<< " $REPLY "
}


#
# Whitelist check
#
whitelist() {
:
}

# Make a module token from command name
pick_token() {
local funtag="[${FUNCNAME[0]}]"
local	token

	case $1 in
		Watch*)	token=$(
			awk '{split($0,parts,"Watch"); print parts[2] }' <<< $1
			)
		;;
		Geo*)	token='GEO'
	;;
	esac
	
	echo "$token"
}

#
# Remove IP addr from database and ipsets
#
freehost() {
local funtag="[${FUNCNAME[0]}]"
local	haveit

	haveit=`$SQL "select IP from $TABLE where IP='$1';"`
	if [ ! -z "$haveit" ]
	then    $SQL "delete from $TABLE where IP='$1';"

		# Cleanup in firewall
		$IPSET -exist del $ME-DB $1
		$IPSET -exist del custody $1
		$IPSET -exist del tarpit $1
		log	"$funtag Removing $1 from $ME database $DB"
		trace	"$funtag Removing $1 from $ME database $DB"
	fi
}

#
# Create an ipset with DROP target at OSI-Layer 5
mk-ipset() {
# $1 ..	the ipset to be created
local funtag="[${FUNCNAME[0]}]"
local	setname=$1
local	settype=$2
local	tables='filter'
local	chains="INPUT FORWARD"
local	table chain	

	case $FIREWALL in
		iptables)
			$IPSET -exist create $*
			
#			if $IPTABLES -t filter -C INPUT   -m set --match-set $setname src -j DROP >/dev/null 2>&1
#			then : echo "$setname already linked with xtables"
#			else $IPTABLES -t filter -I INPUT   -m set --match-set $setname src -j DROP >/dev/null 2>&1
#			fi
#
#			if $IPTABLES -t filter -C FORWARD -m set --match-set $setname src -j DROP >/dev/null 2>&1
#			then : echo "$setname already linked with xtables"
#			else $IPTABLES -t filter -I FORWARD -m set --match-set $setname src -j DROP >/dev/null 2>&1
#			fi
			for table in $tables
			do	
				for chain in $chains
				do
					if $IPTABLES -t $table -C $chain -m set --match-set $setname src -j DROP >/dev/null 2>&1
					then : echo "$setname already linked with xtables"
					else $IPTABLES -t $table -I $chain -m set --match-set $setname src -j DROP >/dev/null 2>&1
				fi
				done
			done
		;;
		firewalld)	# DON'T USE !!!!
			firewall-cmd --permanent --new-ipset=$setname --type="$settype"
			firewall-cmd --permanent --zone=public --add-source=ipset:$setname
			firewall-cmd --reload
		;;
		*)	logger	"[$ME:$$] Error - firewall unknown ..."
			trace	"Error - firewall unknown ..."
	;;
	esac
}

# Create an ipset with DROP target below OSI-Layer 5 in raw + mangle
mk-ipset_lowlevel() {
# $1 .. the ipset to be created
local funtag="[${FUNCNAME[0]}]"
local	setname=$1
local	settype=$2
local	tables="raw mangle"
local	chains="PREROUTING"

	case $FIREWALL in
		iptables)
			$IPSET -exist create $*

			for t in $tables
			do	
				if $IPTABLES -t $t -C PREROUTING -m set --match-set $setname src -j DROP >/dev/null 2>&1
				then : echo "$setname already linked with xtables($t)"
				else $IPTABLES -t $t -I PREROUTING -m set --match-set $setname src -j DROP >/dev/null 2>&1
				fi
			done
		;;
		*)      logger  "[$ME:$$] Error - firewall unknown ..."
			trace   "Error - firewall unknown ..."
	;;
	esac
}


# Make a grep -E 'string|string|..." list from superfluous_map file
mk_superfluous() {
local funtag="[${FUNCNAME[0]}]"
	awk '
	BEGIN   { str="" }
	/^[ \t]*($|#)/	{ next }
        	        { str=str $0 "|" }
	END {	gsub(/[|]$/,"",str)
		print str
	}
	' < rules/superfluous_map
	trace "$funtag (Re)building superfluous_map"
}

#
# Build differences files '<set>.new' and '<set>.del'
# from current ipset members and a drop file
# - probably empty lines
# - probably even indented comment lines starting with
#   '[ \t]\#'
#
mk_ip-diffs() {
# $1    ipset name
# $2    path to new drop file; IPs in column 1
local funtag="[${FUNCNAME[0]}]"
local   this_set=$1
local   dropfile=$2
local   droplist="$POOL/$this_set".droplist
local   currfile="$POOL/$this_set".current
local   newfile="$POOL/$this_set".new
local   delfile="$POOL/$this_set".del

        rm -f $POOL/$this_set.*

        # Keep current members of IPSET in a warm place
        ipset l $this_set | grep '^[1-9]' | awk '{print $1}'| sort -V > $currfile

        # Assure natural IP sort order of droplist as well
        grep '^[1-9]' $dropfile | awk '{print $1}' | sort -V > $droplist

        diff -c $currfile $droplist     |\
        awk     -v newfile=$newfile     \
                -v delfile=$delfile     '
        /^[+][[:space:]]/       { print $2 > newfile }
        /^[-][[:space:]]/       { print $2 > delfile }
        '
}


compress_filter() {
local funtag="[${FUNCNAME[0]}]"
	if [ -f filter ]
	then
		awk '
		/^[ \t]*($|#)/	{ next }
				{ print }
		' filter > filter.compress
	fi
}

dump_looprate() {
local funtag="[${FUNCNAME[0]}]"
local	loop_rate loop_secs loop_mils loop_days loop_rest 
local	hum_rate

	# Need one loop ahead to calculate a difference
	if [ ! -z "$FILT_END" ]
	then
		# Loop rate in ms ...
		loop_rate=$(( `date +%s%3N` - FILT_END ))
		loop_secs=$(( loop_rate/1000 ))
		loop_mils=`printf "%03d" $((loop_rate%1000))`
		loop_days=$(( loop_secs/86400 ))
		loop_rest=$(( loop_secs%86400 ))
		hum_rate="`date -u -d @$loop_rest +%T`.$loop_mils"
		trace   "« Loop rate: $loop_days d, $hum_rate - $loop_rate ms"
	fi
}

dump_runtime() {
local funtag="[${FUNCNAME[0]}]"
local	rtfile=RUNTIME
local	oform="%-15s: %-s\n"
local	rdstat

	rdstat=`mount | grep '^Watcher' | awk '{ print $1,$3}'`

	> $rtfile
	printf 	"$oform" "Object:"	$ME		>> $rtfile

	# FIXME: Ddump here the contents of watchermap.conf
	printf 	"$oform" "MasterPath:"	$MASTER_PATH	>> $rtfile
	printf 	"$oform" "Pool:"	$POOL		>> $rtfile
	printf 	"$oform" "RAMdisk:"	"$rdstat"	>> $rtfile
	printf 	"$oform" "Path:"	$PATH		>> $rtfile
	printf 	"$oform" "Toolslink:"	$TOOLS_LINK	>> $rtfile
	printf 	"$oform" "AWKPATH:"	$AWKPATH	>> $rtfile
	printf 	"$oform" "BASHLIB:"	$BASHLIB	>> $rtfile
	printf 	"$oform" "\$IPSET:"	$IPSET		>> $rtfile

	# FIXME: Dump here the circumstances
	printf 	"$oform" "awk:"		`which awk`	>> $rtfile
	printf 	"$oform" "date:"	`which date`	>> $rtfile
	printf 	"$oform" "dig:"		`which dig`	>> $rtfile
	printf 	"$oform" "grep:"	`which grep`	>> $rtfile
	printf 	"$oform" "ipset:"	`which ipset`	>> $rtfile
	printf 	"$oform" "logger:"	`which logger`	>> $rtfile
	printf 	"$oform" "sqlite:"	`which sqlite3`	>> $rtfile
}


#
# Provide watcher counters chain to iptables
#
mk_watchercounters() {
local funtag="[${FUNCNAME[0]}]"
local	name=WATCHCNT
local	haveit
local	ports_login="22"
local	ports_mta="25 465 587"
local	ports_mbox="110 143 993 995"
local	ports_web="80 443"
local	ports_other=""
local	port_group_name
local	port_list
local	added_ports

	# Check if the watcher counter chain already exists
	haveit=$(iptables -nL INPUT | grep -w $name)

	if [ -z "$haveit" ]
	then
		echo "$funtag Creating $name chain..."
		iptables -N $name

		# Traverse port groups and add rules
		for port_group in "login:$ports_login" "mta:$ports_mta" "mbox:$ports_mbox" "web:$ports_web" "other:$ports_other"
		do
			port_group_name=${port_group%%:*}
			port_list=${port_group#*:}
			added_ports=""

			for port in $port_list
			do
				iptables -A $name -p tcp -m state --state NEW --dport "$port" -j RETURN
 				added_ports+="$port "
			done

			if [ -n "$added_ports" ]
			then
                		#echo "$funtag Added counter(s) for $port_group_name on port(s): $added_ports"
                		printf "%s Added counter(s) for %5s on port(s): %s\n" $funtag $port_group_name "$added_ports"
			fi
		done

		# Link watcher counters into INPUT chain
		echo "$funtag Linking $name to INPUT chain..."
		iptables -I INPUT 1 -j $name
	else
 		echo "$funtag $name chain already exists. Skipping creation."
    	fi
}

# Establish link with ipset 'myrouters' for self-lockout-fencing
# THIS MUST COME LAST to go on top in iptables as 'ACCEPT'!!!)
#
mk_myrouters() {
local funtag="[${FUNCNAME[0]}]"

	ipset -exist create myrouters 'hash:net' 'comment' 'counters'
	if ! $IPTABLES -t raw -nL PREROUTING | grep "match-set myrouters" | grep -q ACCEPT
	then
		$IPTABLES -t raw    -I PREROUTING -m set --match-set myrouters src -j ACCEPT
		$IPTABLES -t mangle -I PREROUTING -m set --match-set myrouters src -j ACCEPT
	else    : echo "IPSET 'myrouters' already established ... skipping"
	fi
}

# Convert time range to milliseconds
# Format: 0d,00:00:00.012
timerange_to_ms() {
local funtag="[${FUNCNAME[0]}]"
local	range="$1"
local	d h m s ms

	IFS=',d:' read -r d h m s <<< "$range"
	ms="${s#*.}"
	s="${s%.*}"

	# 'ms' might be missing ...
	: "${ms:=0}"

	echo $(( ( (d*24 + h)*60 + m )*60*1000 + s*1000 + ms ))
}


# vim: set filetype=sh noexpandtab tabstop=8 shiftwidth=8 autoindent smartindent :
