#!/bin/sh

###########################################################################
#
#	Shell program to parse firewall logs and analyze them with Analog.
#
#	Copyright 2001-2002, Balazs Barany balazs@tud.at
#
#	Version 0.5.1
#
#	This program is free software; you can redistribute it and/or
#	modify it under the terms of the GNU General Public License as
#	published by the Free Software Foundation; either version 2 of the
#	License, or (at your option) any later version. 
#
#	This program is distributed in the hope that it will be useful, but
#	WITHOUT ANY WARRANTY; without even the implied warranty of
#	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
#	General Public License for more details.
#
#	Description:
#
#
#	NOTE: You must be the superuser to run this script or at least have
#		  access to the firewall logs.
#	WARNING!: The output contains security info.  Do not set world-readable.
#
#	Usage:
#
#		fwanalog.sh [ -h | --help ] [-r]
#
#	Options:
#
#		-h, --help	Display this help message and exit.
#		-r              Rotate log cache
#		-t		Only update statistics for today (for hourly use in crontab)
#
#
#	$Id: fwanalog.sh,v 1.28 2002/02/23 21:39:38 bb Exp $
#
#	Revisions:
#
#	2001-04-07	File created
#	2001-04-08	First release 0.1, announced on Freshmeat
#	2001-04-15	Release 0.2: Linux 2.2 ipchains support
#	2001-05-05	Release 0.2.1: Analog 5.0 support, bugfixes
#	2001-06-07	Release 0.2.2: FreeBSD support
#	2001-08-05	Release 0.3: Bugfixes; ICMP support on Linux; onehost=dynip
#	2001-08-18	Version 0.4pre: Speed improvement in the diff phase
#	2001-08-23	Release 0.4: -t option, bugfixes in the pre version
#	2001-11-23	Release 0.4.1: regexp bugfixes in iptables() and ipchains()
# 	2001-12-22	Version 0.5pre: OpenBSD 3.0 pf and Solaris support
# 	2002-02-19	Version 0.5: iptables log-prefix support, portability fixes
# 	2002-02-23	Version 0.5.1: better error handling; analog 5.21 compatible
#
###########################################################################

###########################################################################
#	Constants
###########################################################################

# Script options

PROGNAME=$(basename $0)
VERSION="0.5.1"
# Temp file for this script
# It has been suggested that for maximum security you may wish
# to use ~/tmp instead of /tmp particularly if this script is
# to be used by root.  Create ~/tmp and make sure it is not
# world writable.
TEMP_FILE=/tmp/${PROGNAME}.$$

###########################################################################
#	Variables
###########################################################################

#
# Only update today's page
#
today_only=false		

###########################################################################
#	Commands - Assist in platform portability - with defaults
###########################################################################

sed=${sed:-sed}
perl=${perl:-perl}
grep=${grep:-grep}
egrep=${egrep:-egrep}
zegrep=${zegrep:-zegrep}
analog=${analog:-analog}
date=${date:-date}
sort=${sort:-sort}

###########################################################################
#	Functions
###########################################################################

main ()
{
	# Function to do everything the script normally does.

	# Get today's date for the daily reports.
	TODAY=`$date +%y%m%d`

	# Load the user-settable options from the config file
	. /etc/fwanalog/fwanalog.opts
	
	# create the output directory if necessary, ignore errors
	mkdir -p $outdir
	
	# Parse the logs into a format Analog understands
	($logformat)

	# make sure the "all logs" file exists
	touch $outdir/fwanalog.all.log

	# Find the new lines since the last invocation
	if [ -s $outdir/fwanalog.all.log ]; then
	# there is already an old log - find its last line and use it
	# to determine the new contents of the grepped/converted file
		$grep . $outdir/fwanalog.all.log \
			| tail -1 \
			| $sed 's/[^a-zA-Z0-9 _-]/./g' \
			| $sed 's#^\(.*\)$#1,/^\1$/d#' \
			> $outdir/match_last_line.sed
			# match_last_line.sed now contains the last line in regexp form, so
			# it can be searched in the new file. Most non-alphanumeric chars
			# have been replaced with . so they don't act as metacharacters.

		$grep . $outdir/fwanalog.all.log \
			| tail -1 \
			| $sed 's/[^a-zA-Z0-9 _-]/./g' \
			> $outdir/match_last_line.pattern
			# create the regexp for grep

		# The two "$grep ."-s are for RedHat 7.1 systems with a broken zegrep
		# which appends a blank line at the end of its output
			
		# Check if there is a common part in the old an the new log
		if $grep --silent "`cat $outdir/match_last_line.pattern`" $outdir/fwanalog.current.log ; then
		# there is a common part
			# Delete the common lines in the current log so only the new ones
			# stay and write it to the end of the global log
			$sed -f $outdir/match_last_line.sed $outdir/fwanalog.current.log \
				>> $outdir/fwanalog.all.log
		else 
		# no common part
			cat $outdir/fwanalog.current.log >> $outdir/fwanalog.all.log
		fi
	else
	# There is no old log. We can use the entire current log.
		cp $outdir/fwanalog.current.log $outdir/fwanalog.all.log
	fi

	# Create an empty domain cache for analog so it doesn't complain
	touch $outdir/analog-domains.tab

	# Ask Analog's version number
	analogver=`$analog --help 2>&1 \
		| head -n 1 \
		| $sed 's!^.*\([0-9]\)\.[0-9]*/.*$!\1!'`
	
	# Command line option for the debugging phase: list corrupt logfile entries
	# This is important if bugs appear but doesn't disturb if they don't
	analogopts="$analogopts +V+C"

	# Version-dependent Analog config file
	touch $outdir/fwanalog.analog.conf.ver

	# Don't warn in case of empty reports, this is good for daily reports 
	# in case you're not attacked on this day
	noemptyreportwarning=" +q-R"
	
	# Call analog for today with ascii output, suitable for an e-mailed daily report
	$analog \
			-G +g/usr/share/fwanalog/fwanalog.analog.conf $analogopts \
			-C"DNSFILE $outdir/analog-domains.tab" -C"DNSLOCKFILE $outdir/analog-domains.lck" \
			-C"LOGFILE $outdir/fwanalog.all.log*" \
			-C"OUTFILE $outdir/today.txt" -C"OUTPUT ASCII" -d -W -m -4 -o -z -f -v \
			-C"GOTOS OFF" -C"RUNTIME OFF" -C"LASTSEVEN OFF" \
			+F$TODAY $noemptyreportwarning \
			+g$outdir/fwanalog.analog.conf.ver +g/etc/fwanalog/fwanalog.analog.conf.local \
			2> $outdir/analog.err
			
	# Set special options for Analog version 5 and higher
	if [ $analogver -ge 5 ]
	then
		# Charts go to the output directory, name prefix is "alldates-"
		echo "LOCALCHARTDIR $outdir/alldates-" >> $outdir/fwanalog.analog.conf.ver
		echo "CHARTDIR alldates-" >> $outdir/fwanalog.analog.conf.ver
	fi
	
	# Call analog with all data
	$today_only || $analog \
			-G +g/usr/share/fwanalog/fwanalog.analog.conf $analogopts \
			-C"DNSFILE $outdir/analog-domains.tab" -C"DNSLOCKFILE $outdir/analog-domains.lck" \
			-C"LOGFILE $outdir/fwanalog.all.log*" \
			-C"OUTFILE $outdir/alldates.html" \
			+g$outdir/fwanalog.analog.conf.ver +g/etc/fwanalog/fwanalog.analog.conf.local \
			2>> $outdir/analog.err

	# Set special options for Analog version 5 and higher
	if [ $analogver -ge 5 ]
	then
		# Charts go to the output directory, name prefix is "today-"
		$perl -pwi -e "s!^LOCALCHARTDIR.+!LOCALCHARTDIR $outdir/today-!" \
		   $outdir/fwanalog.analog.conf.ver
		$perl -pwi -e "s!^CHARTDIR.+!CHARTDIR today-!" \
		   $outdir/fwanalog.analog.conf.ver
	fi
	
	# Call analog for today, with the additional quarter-hour-report
	$analog \
			-G +g/usr/share/fwanalog/fwanalog.analog.conf $analogopts \
			-C"DNSFILE $outdir/analog-domains.tab" -C"DNSLOCKFILE $outdir/analog-domains.lck" \
			-C"LOGFILE $outdir/fwanalog.all.log*" \
			-C"OUTFILE $outdir/today.html" -d -W -m \+4 \
			+F$TODAY $noemptyreportwarning \
			+g$outdir/fwanalog.analog.conf.ver +g/etc/fwanalog/fwanalog.analog.conf.local \
			2>> $outdir/analog.err

	# Set special options for Analog version 5 and higher
	if [ $analogver -ge 5 ]
	then
		# Charts go to the output directory, name prefix is "lastweek-"
		$perl -pwi -e "s!^LOCALCHARTDIR.+!LOCALCHARTDIR $outdir/lastweek-!" \
		   $outdir/fwanalog.analog.conf.ver
		$perl -pwi -e "s!^CHARTDIR.+!CHARTDIR lastweek-!" \
		   $outdir/fwanalog.analog.conf.ver
	fi
	
	# Call analog for the last 7 days, with the additional hourly report
	$today_only || $analog \
			-G +g/usr/share/fwanalog/fwanalog.analog.conf $analogopts \
			-C"DNSFILE $outdir/analog-domains.tab" -C"DNSLOCKFILE $outdir/analog-domains.lck" \
			-C"LOGFILE $outdir/fwanalog.all.log*" \
			-C"OUTFILE $outdir/lastweek.html" +H \
			+F-00-00-06 \
			+g$outdir/fwanalog.analog.conf.ver +g/etc/fwanalog/fwanalog.analog.conf.local \
			2>> $outdir/analog.err
			
	# Remove the unnecessary "HTML Conformant" lines from the output
	$perl -pwi -e 's!^.+(validator\.w3\.org/"|nonehtml2\.gif|HTML 2\.0 Conformant).+$!!' \
		$outdir/alldates.html $outdir/today.html $outdir/lastweek.html
			
	keeperrfile=false	
	
	# check if there were corrupt lines
	corruptlines=`$grep "^C: " $outdir/analog.err | wc -l` 
	if [ $corruptlines -ge 1 ]; then
		echo "Analog found $corruptlines corrupt lines. Please consider sending "
		echo "$outdir/analog.err to balazs@tud.at "
		echo "so the author is able to fix the problem."
		keeperrfile=true
	fi
	
	# check if Analog complains of an old language file
	corruptlines=`$grep "error.*language file.*exiting" $outdir/analog.err | wc -l` 
	if [ $corruptlines -ge 1 ]; then
		echo "Analog isn't happy about the language file. Probably you updated"
		echo "to a new version. "
		echo "Use the mklangfile.*.sh scripts in the fwanalog distribution"
		echo "to create a new language file for fwanalog or get the current"
		echo "version of fwanalog (or just the language files) from"
		echo "http://tud.at/programm/fwanalog/"
		keeperrfile=true
	fi
	
	# Check if there is an error which wasn't catched
	corruptlines=`$grep "." $outdir/analog.err | wc -l` 
	if [ $corruptlines -ge 1 ]; then
		if [ "X$keeperrfile" != 'Xfalse' ]; then
		# There was no specific error message
			echo "fwanalog: Analog printed the following error messages:" >> /dev/stderr
			cat "$outdir/analog.err" >> /dev/stderr
		fi
	fi
	
	if [ "X$keeperrfile" != 'Xtrue' ]; then
		# no problem, remove the error log
		rm $outdir/analog.err
	fi

	# Clean up old logfiles
	rm -f $outdir/fwanalog.curr* $outdir/fwanalog.new* $outdir/convdate.sed \
		$outdir/fwanalog.analog.conf.ver $outdir/match_last_line.*
	
	rotate_cache
}

iptables () 
{
	# Parse iptables logfiles into an analog-compatible "URL log"

	$zegrep -h "IN.+OUT.+SRC.+DST.+LEN.+TTL.+PROTO.+(SPT.+DPT|TYPE)" $inputfiles \
		| $sed 's/TYPE=\([0-9]\+\)/SPT= DPT=\1/' \
		> $outdir/fwanalog.current

	mkdateconvscript
	# Create script to convert lines without year to fully specified date

	$sed -f $outdir/convdate.sed $outdir/fwanalog.current > $outdir/fwanalog.current.withyear
	# Use the script on the current logfile

	# Example of converted log line:
	# 2001 Mar 31 00:58:17 www kernel: packet explanation IN=eth1 OUT= MAC=00...:00 SRC=131....38 DST=212....31 LEN=44 \
	#	TOS=0x00 PREC=0x00 TTL=57 ID=58478 PROTO=TCP SPT=61636 DPT=21 WINDOW=16384 RES=0x00 SYN URGP=0 

	# Example of desired output:
	# 131....38 - packet explanation [31/Mar/2001:00:58:17 +0200] "GET /212....31/TCP/21 HTTP/1.0" 200 \
	#	44 "61636" "00....:00" 10 eth1
	#
	# Which means:
	# ip - iptables_log-prefix [date] "GET Desthost/Protocol/Port" 200 PcktLen "http://Sourceport/" "Macadr" 0 interface
	# Sourceport is in the referrer field, macadr in the user-agent, interface
	# in the VirtualHost. (The interface is either IN= or OUT=, never both)
	# There is not always a MAC address, e.g. if the interface is ppp0

	# Decide if the source or the destination host is included in the 
	# Blocked Packet Report (option "onehost" in fwanalog.opts)
	if [ $onehost = true ]; then
		reqhost="\$9"				# The analog "request" contains the source ip
	elif [ $onehost = dynip ]; then
		reqhost="firewall"			# The analog "request" contains this string
	else
		reqhost="\$10"				# The analog "request" contains the destination ip
	fi
	#                1    2       3     4                5                       6        7               8         9            10           11                  12             13       14

	$sort -k 1,1n -k 2,2M -k 3,3n -k4.1,4.2n -k4.3,4.4n -k4.5,4.6n $outdir/fwanalog.current.withyear > $outdir/fwanalog.current.sorted

	$perl -pwe "s!^(\d+) +(\w+) +(\d+) ([0-9:]+) [^:]+: ?([a-zA-Z0-9/.,:_-]*).*IN=(.*) OUT=(\S*) ?M?A?C?=?(.*) SRC=([0-9.]+) DST=([0-9.]+) LEN=(\d+)[^[]+PROTO=([a-zA-Z0-9]+) SPT=(\d*) DPT=(\d+).+\$!\$9 - \$5 [\$3/\$2/\$1:\$4 $timezone] \"GET /$reqhost/\$12/\$14/ HTTP/1.0\" 200 \$11 \"http://\$13/\" \"\$8\" 0 \$6\$7!" \
		$outdir/fwanalog.current.sorted > $outdir/fwanalog.current.log

	# $outdir/fwanalog.current.log now contains the data in the Analog URL format.
}

ipf ()
{
	openbsd
	# For backward compatibility.
	# Initially, I thought that each BSD with ipf uses the same format. Wrong.
}

solarisipf () 
{
        # Adapted from the openbsd function below

	# Parse Solaris ipf syslog files into an analog-compatible "URL log"
	# Tested with Solaris 8 INTEL and ipf 3.4.20

	${zegrep} -h "ipmon.+@.+ -> .+ PR.+len" $inputfiles \
		> $outdir/fwanalog.current

	mkdateconvscript
	# Create script to convert lines without year to fully specified date

	${sed} -f $outdir/convdate.sed $outdir/fwanalog.current \
		> $outdir/fwanalog.current.withyear
	# Use the script on the current logfile
	
	# Example of converted log line:
	#	2001 Apr  5 16:55:55 fw ipmon[1875]: 16:55:54.150871              xl0 @0:2 b 
	#	  217.....93,3819 -> 195.....201,1080 PR tcp len 20 48 -S IN
	# Example of desired output:
	# 217....93 - - [5/Apr/2001:16:55:54 +0200] "GET /195.....201/tcp/1080 HTTP/1.0" 200 \
	#	20 "3819" "" 0 xl0
	#
	# Which means:
	# ip - - [date] "GET Desthost/Protocol/Port" 200 PcktLen "http://Sourceport/" "Macadr" 0 interface
	# Sourceport is in the referrer field, macadr in the user-agent, interface
	# in the VirtualHost. There is no macadr in the BSD log.
	
	# Decide if the source or the destination host is included in the 
	# Blocked Packet Report (option "onehost" in fwanalog.opts)
	if [ $onehost = true ]; then
		reqhost="\$7"				# The analog "request" contains the source ip
	elif [ $onehost =  dynip ]; then
		reqhost="firewall"			# The analog "request" contains this string
	else
		reqhost="\$10"				# The analog "request" contains the destination ip
	fi

	${perl} -pwe \
            's!
                ^							# Begin of line :-)
                    (\d+)\s+(\w+)\s+(\w+)			\s+	# syslog year($1) month($2) day($3)
                    [0-9:]+					\s+	# syslog time
                    [a-zA-Z0-9_.]+				\s+  	# syslog hostname
                    ipmon\[\d+\]:				\s+	# ipmon process identifier
                    \[ID\s+\d+\s+\w+\.\w+\]			\s+	# logging info 
                    ([0-9:]+)\.\d+				\s+	# time($4).hirestime
                    .*\s*(\w+)					\s+	# optional multipler and interface name($5)
                    \@[0-9:]+					\s+	# ruleset
                    .						\s+	# action
                    ([a-zA-Z0-9-_.]+\[)?([0-9.]+)\]?			# optional source name($6), source ip($7)
                    ,?([a-zA-Z0-9\-_]*)				\s+	# source port($8) - may be name or number 
                    -\>						\s+	# the arrow :-)
                    ([a-zA-Z0-9-_.]+\[)?([0-9.]+)\]?			# optional destination name($9), destination ip($10)
                    ,?([a-zA-Z0-9\-_]*)				\s+	# destination port($11)  - may be name or number
                    PR\s+(\w+)					\s+	# protocol($12)	
                    len\s+(\d+)						# length($13)
                    .+							# ignore the rest
                $							# End of line :-)
              !$7 - - [$3/$2/$1:$4 '$timezone'] \"GET /'$reqhost'/$12/$11/ HTTP/1.0" 200 $13 "http://$10/" "" 0 $5!x' \
		$outdir/fwanalog.current.withyear > $outdir/fwanalog.current.log

	# $outdir/fwanalog.current.log now contains the data in the Analog URL format.
}

openbsd () 
{
	# Parse OpenBSD ipf logfiles into an analog-compatible "URL log"
	# Tested with OpenBSD 2.8 ipf.

	$zegrep -h "ipmon.+@.+ -> .+ PR.+len" $inputfiles \
		> $outdir/fwanalog.current

	mkdateconvscript
	# Create script to convert lines without year to fully specified date

	$sed -f $outdir/convdate.sed $outdir/fwanalog.current > $outdir/fwanalog.current.withyear
	# Use the script on the current logfile
	
	# Example of converted log line:
	#	2001 Apr  5 16:55:55 fw ipmon[1875]: 16:55:54.150871              xl0 @0:2 b 
	#	  217.....93,3819 -> 195.....201,1080 PR tcp len 20 48 -S IN
	# Example of desired output:
	# 217....93 - - [5/Apr/2001:16:55:54 +0200] "GET /195.....201/tcp/1080 HTTP/1.0" 200 \
	#	20 "3819" "" 0 xl0
	#
	# Which means:
	# ip - - [date] "GET Desthost/Protocol/Port" 200 PcktLen "http://Sourceport/" "Macadr" 0 interface
	# Sourceport is in the referrer field, macadr in the user-agent, interface
	# in the VirtualHost. There is no macadr in the BSD log.
	
	# Decide if the source or the destination host is included in the 
	# Blocked Packet Report (option "onehost" in fwanalog.opts)
	if [ $onehost = true ]; then
		reqhost="\$6"				# The analog "request" contains the source ip
	elif [ $onehost =  dynip ]; then
		reqhost="firewall"			# The analog "request" contains this string
	else
		reqhost="\$8"				# The analog "request" contains the destination ip
	fi

	#              1       2      3           4               5            6        7            8        9        10         11
	$perl -pwe "s!^(\d+) +(\w+) +(\w+) .+: ([0-9:]+)\.\d+.+ +(\w+) @.+ . ([0-9.]+),?(\d*) -\\> ([0-9.]+),?(\d*) PR (\w+) len (\d+).+\$!\$6 - - [\$3/\$2/\$1:\$4 $timezone] \"GET /$reqhost/\$10/\$9/ HTTP/1.0\" 200 \$11 \"http://\$7/\" \"\" 0 \$5!" \
		$outdir/fwanalog.current.withyear > $outdir/fwanalog.current.log

	# $outdir/fwanalog.current.log now contains the data in the Analog URL format.
}

pf_30 () 
{
	# Parse OpenBSD 3.0 pf logfiles into an analog-compatible "URL log"
	# This *must* happen on an OpenBSD 3.0 system as it requires the OpenBSD
	# version of tcpdump.

	(for log in $inputfiles ; do
		$gzcat -f $log \
		| $tcpdump -n -e -ttt -q -r -
	done) \
		| $egrep -h "rule .+: block .+ on .+ [0-9.]{7}.+" \
		> $outdir/fwanalog.current

	mkdateconvscript
	# Create script to convert lines without year to fully specified date

	$sed -f $outdir/convdate.sed $outdir/fwanalog.current > $outdir/fwanalog.current.withyear
	# Use the script on the current logfile
	
	# Example of converted log line:
	#  TCP:
	# 	2001 Dec 21 17:48:50.760648 rule 12/0(match): block in on ae0: 
	#		192.168.49.2.2081 > 192.168.49.3.22: S 2901914301:2901914301(0) 
	#		win 5840 <mss 1460,sackOK,timestamp 6674376 0,nop,wscale 0> (DF)
	#  UDP:
	#   2001 Dec 20 20:16:24.674266 rule 2/0(match): block in on ae0: 
	#		192.168.49.3.137 > 192.168.49.255.137:  udp 50 (ttl 64, id 61825)
	#  ICMP:
	#	2001 Dec 20 20:21:00.324025 rule 3/0(match): block in on ae0: 
	#		192.168.49.1 > 192.168.49.3: icmp: echo reply (id:23464 seq:2) (ttl 255, id 21394)	
	#
	# Example of desired output:
	# 192.168.49.2 - - [5/Apr/2001:16:55:54 +0200] "GET /192.168.49.3/tcp/22 HTTP/1.0" 
	#	200 20 "2081" "" 0 ae0
	#
	# Which means:
	# ip - - [date] "GET Desthost/Protocol/Port" 200 PcktLen "http://Sourceport/" "Macadr" 0 interface
	# Sourceport is in the referrer field, macadr in the user-agent, interface
	# in the VirtualHost. There is no macadr in the BSD log.
	
	# Decide if the source or the destination host is included in the 
	# Blocked Packet Report (option "onehost" in fwanalog.opts)
	# altreqhost is needed for unknown protocols (e.g. esp, ah)
	if [ $onehost = true ]; then
		reqhost="\$6"				# The analog "request" contains the source ip
		altreqhost="\$7"
	elif [ $onehost =  dynip ]; then
		reqhost="firewall"			# The analog "request" contains this string
		altreqhost="firewall"
	else
		reqhost="\$8"				# The analog "request" contains the destination ip
		altreqhost="\$9"
	fi

	#first TCP, then UDP, then ICMP, then others (hopefully this works)
	#               1      2      3        4                                5        6        7            8       9
	$perl -pwe "s!^(\d+) +(\w+) +(\d+) +([0-9:]+)\.\d+ rule.+block \w+ on (\w+): ([0-9.]+)\.(\d+) \\> ([0-9.]+)\.(\d+): tcp (\d+)(.*)\$!\$6 - - [\$3/\$2/\$1:\$4 $timezone] \"GET /$reqhost/tcp/\$9/ HTTP/1.0\" 200 \$10 \"http://\$7/\" \"\" 0 \$5!" \
		$outdir/fwanalog.current.withyear \
 	|$perl -pwe "s!^(\d+) +(\w+) +(\d+) +([0-9:]+)\.\d+ rule.+block \w+ on (\w+): ([0-9.]+)\.(\d+) \\> ([0-9.]+)\.(\d+): +udp (\d+).*\$!\$6 - - [\$3/\$2/\$1:\$4 $timezone] \"GET /$reqhost/udp/\$9/ HTTP/1.0\" 200 \$10 \"http://\$7/\" \"\" 0 \$5!" \
	|$perl -pwe "s!icmp(.+)echo reply!icmp$1echo_reply!" \
 	|$perl -pwe "s!^(\d+) +(\w+) +(\d+) +([0-9:]+)\.\d+ rule.+block \w+ on (\w+): ([0-9.]+)(X?) \\> ([0-9.]+): icmp: ([a-z]+[a-z]).*\$!\$6 - - [\$3/\$2/\$1:\$4 $timezone] \"GET /$reqhost/icmp/\$9/ HTTP/1.0\" 200 0 \"http://\$7/\" \"\" 0 \$5!" \
 	|$perl -pwe "s!^(\d+) +(\w+) +(\d+) +([0-9:]+)\.\d+ rule.+block \w+ on (\w+): ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\.?(\d*) \\> ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\.?(\d*): (\S*) ?(\d*).*\$!\$6 - - [\$3/\$2/\$1:\$4 $timezone] \"GET /$reqhost/\$10/\$9/ HTTP/1.0\" 200 0\$11 \"http://\$7/\" \"\" 0 \$5!" \
 	|$perl -pwe "s!^(\d+) +(\w+) +(\d+) +([0-9:]+)\.\d+ rule.+block \w+ on (\w+): (\w+) ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\.?(\d*) \\> ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\.?(\d*).*len (\d+).*\$!\$7 - - [\$3/\$2/\$1:\$4 $timezone] \"GET /$altreqhost/\$6\$10/ HTTP/1.0\" 200 \$11 \"http://\$8\" \"\" 0 \$5!" \
		> $outdir/fwanalog.current.log

	# $outdir/fwanalog.current.log now contains the data in the Analog URL format.
}

freebsd () 
{
	# Parse FreeBSD ipf logfiles into an analog-compatible "URL log"
	# Tested with FreeBSD ipf

	$zegrep -h " -> .+ PR.+len" $inputfiles \
		> $outdir/fwanalog.current

	mkmonthconvscript
	# Create script to convert lines with a numeric month to the alphanumeric month (Jan...Dec)

	$sed -f $outdir/convdate.sed $outdir/fwanalog.current \
		> $outdir/fwanalog.current.withmonth
	# Use the script on the current logfile

	# Example of converted log line:
	#	04/06/2001 16:55:55.418398 tun0 @0:2 b 
	#	  217.....93,3819 -> 195.....201,1080 PR tcp len 20 48 -S IN
	# Example of desired output:
	# 217....93 - - [5/Apr/2001:16:55:54 +0200] "GET /195.....201/tcp/1080 HTTP/1.0" 200 \
	#	20 "3819" "" 0 xl0
	#
	# Which means:
	# ip - - [date] "GET Desthost/Protocol/Port" 200 PcktLen "http://Sourceport/" "Macadr" 0 interface
	# Sourceport is in the referrer field, macadr in the user-agent, interface
	# in the VirtualHost. There is no macadr in the BSD log.
	
	# Decide if the source or the destination host is included in the 
	# Blocked Packet Report (option "onehost" in fwanalog.opts)
	if [ $onehost = true ]; then
		reqhost="\$4"				# The analog "request" contains the source ip
	elif [ $onehost =  dynip ]; then
		reqhost="firewall"			# The analog "request" contains this string
	else
		reqhost="\$6"				# The analog "request" contains the destination ip
	fi

	#              1             2              3              4         5          6         7        8         9     
	$perl -pwe "s!^(\d+/\w+/\d+) ([0-9:]+)\.\d+ +(\w+) @.+ . ([0-9.]+),*(\d*) -\\> ([0-9.]+),*(\d*) PR (\w+) len (\d+).+\$!\$4 - - [\$1:\$2 $timezone] \"GET /$reqhost/\$8/\$7/ HTTP/1.0\" 200 \$9 \"http://\$5/\" \"\" 0 \$3!" \
		$outdir/fwanalog.current.withmonth > $outdir/fwanalog.current.log

	# $outdir/fwanalog.current.log now contains the data in the Analog URL format.
}

ipchains () 
{
	# Parse ipchains logfiles into an analog-compatible "URL log"
	# ipchains doesn't write the protocol name into the log, only the protocol number. 
	# So we convert them here manually.

	$zegrep -h "Packet log: .+ DENY .+PROTO=.+L=.+S.+I=.+F=.+T=" $inputfiles \
		| $sed 's/PROTO=1 /PROTO=icmp /' \
		| $sed 's/PROTO=2 /PROTO=igmp /' \
		| $sed 's/PROTO=6 /PROTO=tcp /' \
		| $sed 's/PROTO=17 /PROTO=udp /' \
		> $outdir/fwanalog.current

	mkdateconvscript
	# Create script to convert lines without year to fully specified date

	$sed -f $outdir/convdate.sed $outdir/fwanalog.current > $outdir/fwanalog.current.withyear
	# Use the script on the current logfile

	# Example of converted log line:
	# 2001 Apr 18 06:26:18 extdevel kernel: Packet log: input DENY eth0 PROTO=17 \
	#	193.83.115.48:137 193.83.115.255:137 L=78 S=0x00 I=60301 F=0x0000 T=128 (#9)
	# Example of desired output:
	# 131....38 - - [31/Mar/2001:00:58:17 +0200] "GET /212....31/TCP/21 HTTP/1.0" 200 \
	#	44 "61636" "00....:00" 10 eth1
	#
	# Which means:
	# ip - - [date] "GET Desthost/Protocol/Port" 200 PcktLen "http://Sourceport/" "Macadr" 0 interface
	# Sourceport is in the referrer field, macadr in the user-agent, interface
	# in the VirtualHost. 
	# There is no MAC address in ipchains logs.

	# Decide if the source or the destination host is included in the 
	# Blocked Packet Report (option "onehost" in fwanalog.opts)
	if [ $onehost = true ]; then
		reqhost="\$8"				# The analog "request" contains the source ip
	elif [ $onehost =  dynip ]; then
		reqhost="firewall"			# The analog "request" contains this string
	else
		reqhost="\$10"				# The analog "request" contains the destination ip
	fi

	#                1    2       3     4              5            6                 7        8 	   9        10      11       12 
	$perl -pwe "s!^(\d+) +(\w+) +(\d+) ([0-9:]+) .+(DENY|REJECT) ([a-z0-9]+) PROTO=([\w-]+) ([0-9.]+):(\d*) ([0-9.]+):(\d*) L=(\d+).+\$!\$8 - - [\$3/\$2/\$1:\$4 $timezone] \"GET /$reqhost/\$7/\$11/ HTTP/1.0\" 200 \$12 \"http://\$9/\" \"\" 0 \$6!" \
		$outdir/fwanalog.current.withyear > $outdir/fwanalog.current.log

	# $outdir/fwanalog.current.log now contains the data in the Analog URL format.
}

mkdateconvscript ()
{
	# Creates a sed script in the output dir which converts the firewall logs
	# (that don't have the year specified) to the real year (if your logs 
	# aren't too old)

	currmo=`$date +%m`
	curryear=`$date +%Y`
	lastyear=`echo $curryear | awk '{ print($1 - 1) }'`

	(
	if [ $currmo -ge 1 ]; then echo "s/^Jan/$curryear Jan/"; else echo "s/^Jan/$lastyear Jan/"; fi
	if [ $currmo -ge 2 ]; then echo "s/^Feb/$curryear Feb/"; else echo "s/^Feb/$lastyear Feb/"; fi
	if [ $currmo -ge 3 ]; then echo "s/^Mar/$curryear Mar/"; else echo "s/^Mar/$lastyear Mar/"; fi
	if [ $currmo -ge 4 ]; then echo "s/^Apr/$curryear Apr/"; else echo "s/^Apr/$lastyear Apr/"; fi
	if [ $currmo -ge 5 ]; then echo "s/^May/$curryear May/"; else echo "s/^May/$lastyear May/"; fi
	if [ $currmo -ge 6 ]; then echo "s/^Jun/$curryear Jun/"; else echo "s/^Jun/$lastyear Jun/"; fi
	if [ $currmo -ge 7 ]; then echo "s/^Jul/$curryear Jul/"; else echo "s/^Jul/$lastyear Jul/"; fi
	if [ $currmo -ge 8 ]; then echo "s/^Aug/$curryear Aug/"; else echo "s/^Aug/$lastyear Aug/"; fi
	if [ $currmo -ge 9 ]; then echo "s/^Sep/$curryear Sep/"; else echo "s/^Sep/$lastyear Sep/"; fi
	if [ $currmo -ge 10 ]; then echo "s/^Oct/$curryear Oct/"; else echo "s/^Oct/$lastyear Oct/"; fi
	if [ $currmo -ge 11 ]; then echo "s/^Nov/$curryear Nov/"; else echo "s/^Nov/$lastyear Nov/"; fi
	if [ $currmo -ge 12 ]; then echo "s/^Dec/$curryear Dec/"; else echo "s/^Dec/$lastyear Dec/"; fi
	) > $outdir/convdate.sed
}

mkmonthconvscript ()
{
	# Creates a sed script in the output dir which converts the firewall logs
	# (that have the month specified numerically) to the month's abbreviation (Jan...Dec)

	(
	echo "s!/01/!/Jan/!"
	echo "s!/02/!/Feb/!"
	echo "s!/03/!/Mar/!"
	echo "s!/04/!/Apr/!"
	echo "s!/05/!/May/!"
	echo "s!/06/!/Jun/!"
	echo "s!/07/!/Jul/!"
	echo "s!/08/!/Aug/!"
	echo "s!/09/!/Sep/!"
	echo "s!/10/!/Oct/!"
	echo "s!/11/!/Nov/!"
	echo "s!/12/!/Dec/!"
	) > $outdir/convdate.sed
}

rotate_cache ()
{
	# MODIFIED by Debian Maintainer to NOT store output in a different file,
	# but simply discard it.

	# Greps all entries not from the current month from $outdir/fwanalog.all.log
	# to another file. This is good because if fwanalog.all.log is smaller, it 
	# can be diffed faster. However, this is entirely optional.

#	echo "Note: rotating is not necessary anymore!"

	# Load the user-settable options from the config file
	source /etc/fwanalog/fwanalog.opts
	
	# Month and year as they appear in the web server log
	grepdate=`$date +/%b/%Y:`
	# Name to indicate that this file is older
#	newlogname=fwanalog.all.log.`$date +%Y-%m`

#	$grep -vh $grepdate $outdir/fwanalog.all.log > $outdir/$newlogname
#	echo "$grep -vh $grepdate $outdir/fwanalog.all.log > $outdir/$newlogname"
	$grep -h $grepdate $outdir/fwanalog.all.log > $outdir/fwanalog.all.log.current
#	$echo "$grep -h $grepdate $outdir/fwanalog.all.log > $outdir/fwanalog.all.log.current"

	rm $outdir/fwanalog.all.log
#	echo "rm $outdir/fwanalog.all.log"
	mv $outdir/fwanalog.all.log.current $outdir/fwanalog.all.log
#	echo "mv $outdir/fwanalog.all.log.current $outdir/fwanalog.all.log"
}

clean_up ()
{

	#####	
	#	Function to remove temporary files and other housekeeping
	#	No arguments
	#####

	rm -f ${TEMP_FILE}
}


graceful_exit ()
{
	#####
	#	Function called for a graceful exit
	#	No arguments
	#####

	clean_up
	exit
}


error_exit () 
{
	#####	
	# 	Function for exit due to fatal program error
	# 	Accepts 1 argument
	#		string containing descriptive error message
	#####

	
	echo "${PROGNAME}: ${1:-"Unknown Error"}" >&2
	clean_up
	exit 1
}


term_exit ()
{
	#####
	#	Function to perform exit if termination signal is trapped
	#	No arguments
	#####

	echo "${PROGNAME}: Terminated"
	clean_up
	exit
}


int_exit ()
{
	#####
	#	Function to perform exit if interrupt signal is trapped
	#	No arguments
	#####

	echo "${PROGNAME}: Aborted by user"
	clean_up
	exit
}


usage ()
{
	#####
	#	Function to display usage message (does not exit)
	#	No arguments
	#####

	echo "Usage: ${PROGNAME} [-h | --help] [-r] [-t]"
}


helptext ()
{
	#####
	#	Function to display help message for program
	#	No arguments
	#####
	
	local tab=$(echo -en "\t\t")
		
	cat <<- -EOF-

	${PROGNAME} ver. ${VERSION}	
	This is a program to parse firewall logs and analyze them with Analog.
	
	$(usage)
	
	Options:
	
	-h, --help	Display this help message and exit.
	-r              Rotate log cache (not necessary anymore)
	-t              Only update statistics for today (e.g. for hourly use)
			
	NOTE: You must be the superuser to run this script, or have at least 
	      read rights to the firewall log.
	WARNING!: Contains security info.  Do not set world-readable.	
-EOF-
}	


###########################################################################
#	Program starts here
###########################################################################

# Trap TERM, HUP, and INT signals and properly exit

trap term_exit TERM HUP
trap int_exit INT

# Process command line arguments

if [ "$1" = "--help" ]; then
	helptext
	graceful_exit
fi

# Process arguments - edit to taste

while getopts ":hrt" opt; do
	case $opt in
		r )	rotate_cache
			graceful_exit ;;
		h )	helptext
			graceful_exit ;;
		t )	today_only=true ;;
		* )	usage
			exit 1
	esac
done

# No arguments - normal case
if [ $OPTIND -gt $# ]; then
	(main)
fi

graceful_exit

