#!/bin/sh

###########################################################################
#
#	Shell program to parse firewall logs and analyze them with Analog.
#
#	Copyright 2001-2002, Balazs Barany balazs@tud.at
#
#	Version 0.6.2
#
#	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. (See README.sudo for a solution.)
#
#	Usage:
#
#		fwanalog.sh [ -h | --help ] [-r] [-a IP-addr] [-p packet]
#
#	Options:
#
#		-h, --help	Display this help message and exit.
#		-r          Rotate log cache
#		-t          Only update statistics for today (for hourly use in crontab)
#	                  The sep_hosts and sep_packets commands in fwanalog.opts
#	                  are ignored.
#		-a IP-addr  Create a separate report for this host 
#		-p packet   Create a separate report for this packet 
#                       Format: target/protocol/portnumber
#                       e.g. 192.168.0.1/tcp/21 or firewall/udp/137		
#
#
#	$Id: fwanalog.sh,v 1.47 2003/01/08 15:52:16 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
# 	2002-03-03	Version 0.5.2: added ZyNOS parser
# 	2002-03-07	Version 0.6pre: optional separate reports for each packet 
#                               and host
#	2002-04-28	Version 0.6: integrated change requests from lots of people
#	2002-04-28	Version 0.6.1: some bugfixes with packet/host report generation
#	2003-01-03	Version 0.6.2pre1: Support for Cisco PIX firewall logs
#	2003-01-08	Version 0.6.2: Released as pix() seems to work and because
#								the new analog version requires a new langfile
#
###########################################################################

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

# Script options

PROGNAME=$(basename $0)
VERSION="0.6.2"

###########################################################################
#	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}

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

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

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

	# change into /usr/local/etc/fwanalog
	cd /usr/local/etc/fwanalog
	
	# Load the user-settable options from the config file
	. ./`basename $0 | $sed 's/$/.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

			# Save the new lines in current.log.1 and move that over current.log
			$sed -f $outdir/match_last_line.sed $outdir/fwanalog.current.log \
				> $outdir/fwanalog.current.log.1
			mv $outdir/fwanalog.current.log.1 $outdir/fwanalog.current.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./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./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./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./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./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./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./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./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|png)|HTML 2\.0 Conformant).+$!!' \
		$outdir/alldates.html $outdir/today.html $outdir/lastweek.html
		
	# If only todays report is generated, don't create separate host and packet
	# reports unless $host_to_report or $packet_to_report is set (checked later)
	if $today_only; then
		sep_hosts=false
		sep_packets=false
	fi
		
	# Check if -a was used: create a separate report.
	if [ X"$host_to_report" != X ]; then
		sep_hosts=true
		# must create the separate host report
	fi
		
	# If configured, create separate logs for each host from the current log or
	# for the host given with -a
	if [ "X$sep_hosts" = "Xtrue" ]; then
		
		# The following characters are allowed in domain names - this is
		# important because people could (perhaps) manipulate their reverse DNS
		# to point to "../../../etc/passwd>.../something" and we would then, 
		# as root, overwrite that file with a report.
		hostchars='a-zA-Z0-9._-'

		if [ X"$host_to_report" = X ]; then
		# Create a list of unique IPs in the current log
			$sed 's/^\([0-9.]*\) .*$/\1/' $outdir/fwanalog.current.log \
				| sort -u \
				> $outdir/fwanalog.current.hosts.log
		else
		# Just use the provided IP address for the report
			if $egrep --silent " ([0-9].){4} $host_to_report" $outdir/analog-domains.tab; then
			# The address given on the command line is a domain name (not an
			# IP), so extract the IP address.
				host_to_report=`$egrep "([0-9].){4} $host_to_report" \
									$outdir/analog-domains.tab \
									| $perl -pwe "s/^\\d+ ([0-9.]+) $host_to_report/\$1/i"`
			fi
			
			echo "$host_to_report" > $outdir/fwanalog.current.hosts.log
		fi

		mkdir -p $outdir/hosts

		# Create a separate report for each host
		for host in `cat $outdir/fwanalog.current.hosts.log`; do
			
			# Determine the dns name of this host, if existent
			hostname=`$egrep "$host [$hostchars]+\$" $outdir/analog-domains.tab \
				| $perl -pwe 's/^[0-9]* [0-9.]* (.*)$/\L$1/' `
			#hostname can contain only the allowed characters ($hostchars).
			#It is lowercased because analog also lowercases the names.

			# Use the IP address if the name couldn't be resolved
			if [ X"$hostname" = "X" ]; then
				hostname=$host
			fi

			# Set special options for Analog version 5 and higher
			if [ $analogver -ge 5 ]
			then
				# Charts go to the output directory, name prefix is "hosts/NAME-"
				$perl -pwi -e "s!^LOCALCHARTDIR.+!LOCALCHARTDIR $outdir/hosts/$hostname-!" \
				   $outdir/fwanalog.analog.conf.ver
				$perl -pwi -e "s!^CHARTDIR.+!CHARTDIR $hostname-!" \
				   $outdir/fwanalog.analog.conf.ver
			fi
	
			# Call analog with all data
			$analog \
					-G +g./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/hosts/$hostname.html" \
					-C"ORGANISATION OFF" -C"DOMAIN OFF" \
					-C"HOSTINCLUDE $hostname" \
					+g$outdir/fwanalog.analog.conf.ver +g./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|png)|HTML 2\.0 Conformant).+$!!' \
				$outdir/hosts/$hostname.html
		done

	fi
			
	# Check if -p was used: create a separate packet report.
	if [ X"$packet_to_report" != X ]; then
		sep_packets=true
		# must create the separate packet report
	fi
		
	# If configured, create separate logs for each packet from the current log
	# or for the packet given with -p
	if [ "X$sep_packets" = "Xtrue" ]; then
		
		if [ X"$packet_to_report" = X ]; then
		# Create a list of unique packets in the current log, and remove
		# slashes from the end (for protocols without a port number)
			$sed 's!^.*"GET /\([0-9a-zA-Z./]*\)/ HTTP.*$!\1!' $outdir/fwanalog.current.log \
				| sort -u \
				| $sed 's!/$!!' \
				> $outdir/fwanalog.current.packets.log
		else
		# Just use the provided packet for the report
			echo "$packet_to_report" > $outdir/fwanalog.current.packets.log
		fi

		mkdir -p $outdir/packets

		# Create a separate report for each packet
		for packet in `cat $outdir/fwanalog.current.packets.log`; do
			
			# Convert the packet into a matching pattern for analog's FILEINCLUDE
			analogmatch=`echo $packet \
				| $perl -pwe 's!(firewall|[0-9.]+)/([a-z]+)/([0-9]+)!/$1/$2/*($3)/*!gi'`

			# Convert the packet, which contains slashes, into a
			# filesystem-friendly form
			fsform=`echo $packet | $sed 's!/!-!g'`
			
			# Set special options for Analog version 5 and higher
			if [ $analogver -ge 5 ]
			then
				# Charts go to the output directory, name prefix is "packets/PACKET-"
				$perl -pwi -e "s!^LOCALCHARTDIR.+!LOCALCHARTDIR $outdir/packets/$fsform-!" \
				   $outdir/fwanalog.analog.conf.ver
				$perl -pwi -e "s!^CHARTDIR.+!CHARTDIR $fsform-!" \
				   $outdir/fwanalog.analog.conf.ver
			fi
	
			# Call analog with all data
			$analog \
					-G +g./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/packets/$fsform.html" \
					-C"FILEINCLUDE /$packet/*" \
					-C"FILEINCLUDE $analogmatch" \
					+g$outdir/fwanalog.analog.conf.ver +g./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|png)|HTML 2\.0 Conformant).+$!!' \
				$outdir/packets/$fsform.html
		done
	fi

	# Change hosts in each generated report to point to the page about this host
	
	# Search for host reports in the output directory and edit the output
	# files to link to them
	for hostlog in $outdir/hosts/*.html; do

		# Get the hostname from the filename
		hostname=`echo $hostlog | $sed 's/^.*hosts.\(.*\).html/\1/' `

		if [ X"$hostname" != "X*" ]; then
		# there are files
		
			# Replace all hosts with a URL pointing to the separate report
			$perl -pwi -e \
				"s!(\\d+): ($hostname)\$!\$1: <a href=\"hosts/\$2.html\">\$2</a>!i" \
				$outdir/alldates.html $outdir/lastweek.html $outdir/today.html

			# Do the same in each file in the packet directory
			for packetlog in `$egrep -l "$hostname" $outdir/packets/*.html 2> /dev/null`; do

				if [ -e "$packetlog" ]; then
					$perl -pwi -e \
						"s!(\\d+): ($hostname)\$!\$1: <a href=\"../hosts/\$2.html\">\$2</a>!i" \
						"$packetlog"
				fi
			done
		fi
	done

	# The same for packets
	
	# Search for packet reports in the output directory and edit the output
	# files to link to them
	for packetlog in $outdir/packets/*.html; do

		# Get the packet from the filename
		packet=`echo $packetlog \
			| $sed 's!^.*/packets/\(.*\).html$!\1!' \
			| $sed 's!-!/!g' `

		# Get the relative filename
		packetfile=`echo $packetlog \
			| $sed 's!^.*/packets/\(.*.html\)$!\1!' `

		# Convert the first form into a matching pattern
		packetform1=`echo $packet \
			| $perl -pwe 's!^(firewall|[0-9.]+)/([a-z0-9]+)/([0-9]*)$!$1:$3/$2!i'`
		# Convert the second form into a matching pattern
		packetform2=`echo $packet \
			| $perl -pwe 's!^(firewall|[0-9.]+)/([a-z0-9]+)/([0-9]*)$!$1:[a-z0-9_*-]+ \\\\($3\\\\)/$2!i'`

		if [ X"$packet" != "X*" ]; then
		# there are packet logs

			# Replace all packets with a URL pointing to the separate report
			# - both possible forms
			$perl -pwi -e \
				"s!(\\d+: +)($packetform1)\$!\$1<a href=\"packets/$packetfile\">\$2</a>!i" \
				$outdir/alldates.html $outdir/lastweek.html $outdir/today.html
			$perl -pwi -e \
				"s!(\\d+: +)($packetform2)\$!\$1<a href=\"packets/$packetfile\">\$2</a>!i" \
				$outdir/alldates.html $outdir/lastweek.html $outdir/today.html

			# Do the same in each host log
			for hostlog in `$egrep -l "$packetform1|$packetform2" $outdir/hosts/*.html 2> /dev/null`; do

				if [ -e "$hostlog" ]; then

					$perl -pwi -e \
						"s!(\\d+: +)($packetform1)\$!\$1<a href=\"../packets/$packetfile\">\$2</a>!i" \
						"$hostlog"
					$perl -pwi -e \
						"s!(\\d+: +)($packetform2)\$!\$1<a href=\"../packets/$packetfile\">\$2</a>!i" \
						"$hostlog"
				fi
			done
		fi
	done
	
	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*.log $outdir/convdate.sed \
		$outdir/fwanalog.analog.conf.ver $outdir/match_last_line.*
}

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

	$zegrep -h "IN.+OUT.+SRC.+DST.+LEN.+TTL.+PROTO.+" $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
	$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.withyear > $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.+@[0-9:]+ b.+ -> .+ 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.+@[0-9:]+ b.+ -> .+ 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+ *[0-9]*x? +(\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.
}

zynos () 
{
	# Parse ZynOS (ZyXEL, NETGEAR) logfiles into an analog-compatible "URL log"

	# This pattern excludes "last message repeated X times" lines
	# so the count will be artificially low.  How to handle?!?
	$zegrep -h "IP.+Src.+Dst.+(ICMP|TCP|UDP).+spo.+dpo.+" $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:
	# 2002 Feb 27 14:43:51 router host: IP[Src=164....2 Dst=65....189 TCP \
	#	spo=02945  dpo=00080]}S03>R02mD

	# Example of desired output:
	# 164....2 - - [27/Feb/2002:14:43:51 +0500] "GET /65....189/TCP/80 HTTP/1.0" 200 \
	#	1 "http://2945/" "" 0 router
	#
	# Which means:
	# SrcIP - - [date] "GET ReqHost/Protocol/DstPort HTTP/1.0" 200 
	#	FakePacketLen "http://SrcPort/" "" 0 routerName
	# SrcPort is in the referrer field, routerName in the VirtualHost.
	# There is no MAC address or packet length in NETGEAR/ZyXEL 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="\$6"		# The analog "request" contains the source ip
	elif [ $onehost = dynip ]; then
		reqhost="firewall"	# The analog "request" contains this string
	else
		reqhost="\$7"		# The analog "request" contains the destination ip
	fi
	#               1      2      3     4         5                     6               7           8            9               10         11
	$perl -pwe "s!^(\d+) +(\w+) +(\d+) ([0-9:]+) (\S+) [^:]+: +IP\[Src=([0-9\.]+) +Dst=([0-9\.]+) +(\S+) +spo=0*([0-9]+) +dpo=0*([0-9]+)\]}(\S+)\$!\$6 - - [\$3/\$2/\$1:\$4 $timezone] \"GET /$reqhost/\$8/\$10/ HTTP/1.0\" 200 1 \"http://\$9/\" \"\" 0 \$5!" \
		$outdir/fwanalog.current.withyear > $outdir/fwanalog.current.log

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

pix () 
{
	# Parse Cisco PIX logfiles into an analog-compatible "URL log"
	# Tested with a log from Cisco PIX version 6.22/IOS
	# Adapted from the solarisipf() function

	# Note: Cisco doesn't log packet lengts so each packet is faked to have 0 byte.
	# See Analog's SIZE and *COLS commands to turn off packet size reports.

	pixpatterns="Inbound TCP connection denied from [0-9./]+ to [0-9./]+"
	pixpatterns="$pixpatterns|Deny inbound (udp|icmp|tcp) from [0-9./]+ to [0-9./]+"
	pixpatterns="$pixpatterns|Deny inbound \(No xlate\) (udp|icmp|tcp) src [^:]+:[0-9./]+ dst [^:]+:[0-9./]"
	pixpatterns="$pixpatterns|translation creation failed for (udp|icmp|tcp) src [^:]+:[0-9./]+ dst [^:]+:[0-9./]+"
	pixpatterns="$pixpatterns|No translation group found for (udp|icmp|tcp) src [^:]+:[0-9./]+ dst [^:]+:[0-9./]+"
	${zegrep} -hi "$pixpatterns" $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
	
	# Examples of converted log lines:
	#	2002 Dec 24 09:14:18 example.com Dec 24 2002 15:14:18: 
	#		%PIX-2-306001: Inbound TCP connection denied from 
	#		10.206.26.58/4011 to 10.96.160.115/80 flags SYN  on interface outside 
	#	2002 Dec 24 09:05:40 example.com Dec 24 2002 15:05:40: 
	#		%PIX-2-306006: Deny inbound UDP 
	#		from 10.114.112.73/1028 to 10.96.160.196/137 on interface outside 
	#	2002 Dec 24 07:18:05 example.com Dec 24 2002 13:18:05: 
	#		%PIX-3-306011: Deny inbound (No xlate) icmp 
	#		src outside:10.249.118.254 dst outside:10.96.160.84 (type 8, code 0) 

	# 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="\$5"				# The analog "request" contains the source ip
		reqhost_1="\$7"				# in a more complex regexp is the position $7 instead of $5
		reqhost_1="\$6"				# ... or $6
	elif [ $onehost =  dynip ]; then
		reqhost="firewall"			# The analog "request" contains this string
	else
		reqhost="\$7"				# The analog "request" contains the destination ip
		reqhost_1="\$10"			# in a more complex regexp is the position $10 instead of $7
		reqhost_2="\$8"				# ... or $8
	fi

	cat $outdir/fwanalog.current.withyear \
		| ${perl} -pwe \
            's! 				# First, the TCP lines
                ^							# Begin of line :-)
                    (\d+)\s(\w+)\s+(\w+)	\s+	# syslog year $1 month $2 day $3
                    ([0-9:]+)				\s+	# syslog time $4
                    [a-zA-Z0-9_.-]+			\s+	# syslog hostname
					\w\w\w \s+ \d+ \s+ \d+ \s+ [0-9:]+ \s # PIX date/time in UTC
					.+ Inbound.TCP.connection.denied \s	# PIX ID (?); verbose description
					from \s ([0-9.]+)/([0-9]+)	\s		# Source IP ($5), port ($6)
					to \s ([0-9.]+)/([0-9]+)	\s		# Destination IP ($7), port ($8)
                    flags \s ([A-Z 	]+) \s				# TCP flags ($9)
					on.interface.([a-zA-Z0-9_-]+)	\s?	# interface ($10), possible whitespace
                $							# End of line :-)
              !$5 - - [$3/$2/$1:$4 '$timezone'] \"GET /'$reqhost'/tcp/$8/ HTTP/1.0" 200 0 "http://$6/" "" 0 $10!x' \
		| ${perl} -pwe \
            's! 				# Deny inbound UDP, first version (with "on interface")
                ^							# Begin of line :-)
                    (\d+)\s(\w+)\s+(\w+)	\s+	# syslog year $1 month $2 day $3
                    ([0-9:]+)				\s+	# syslog time $4
                    [a-zA-Z0-9_.-]+			\s+	# syslog hostname
					\w\w\w \s+ \d+ \s+ \d+ \s+ [0-9:]+ \s # PIX date/time in UTC
					.+ Deny.inbound.UDP \s	# PIX ID (?); verbose description
					from \s ([0-9.]+)/([0-9]+)	\s		# Source IP ($5), port ($6)
					to \s ([0-9.]+)/([0-9]+)	\s		# Destination IP ($7), port ($8)
					on.interface \s ([a-zA-Z0-9_-]+)?	# interface ($9), 
					.*									# possible whitespace or junk
                $							# End of line :-)
              !$5 - - [$3/$2/$1:$4 '$timezone'] \"GET /'$reqhost'/udp/$8/ HTTP/1.0" 200 0 "http://$6/" "" 0 $9!x' \
		| ${perl} -pwe \
            's! 				# Deny inbound UDP, second version (without "on interface")
                ^							# Begin of line :-)
                    (\d+)\s(\w+)\s+(\w+)	\s+	# syslog year $1 month $2 day $3
                    ([0-9:]+)				\s+	# syslog time $4
                    [a-zA-Z0-9_.-]+			\s+	# syslog hostname
					\w\w\w \s+ \d+ \s+ \d+ \s+ [0-9:]+ \s # PIX date/time in UTC
					.+ Deny.inbound.UDP \s	# PIX ID (?); verbose description
					from \s ([0-9.]+)/([0-9]+)	\s		# Source IP ($5), port ($6)
					to \s ([0-9.]+)/([0-9]+)	\s		# Destination IP ($7), port ($8)
					.*									# possible whitespace or junk
                $							# End of line :-)
              !$5 - - [$3/$2/$1:$4 '$timezone'] \"GET /'$reqhost'/udp/$8/ HTTP/1.0" 200 0 "http://$6/" "" 0 unknown!x' \
		| ${perl} -pwe \
            's! 				# Deny inbound (No xlate) (tcp|udp)
                ^							# Begin of line :-)
                    (\d+)\s(\w+)\s+(\w+)	\s+	# syslog year $1 month $2 day $3
                    ([0-9:]+)				\s+	# syslog time $4
                    [a-zA-Z0-9_.-]+			\s+	# syslog hostname
					\w\w\w \s+ \d+ \s+ \d+ \s+ [0-9:]+ \s # PIX date/time in UTC
					.+.Deny.inbound..No.xlate..(udp|tcp) \s	# PIX ID (?); desc; protocol ($5)
					src \s (\w+):([0-9.]+)/([0-9]+)	\s	# Interface $6, Source IP $7, port $8
					dst \s (\w+):([0-9.]+)/([0-9]+)		# Interface $9, Dest IP $10, port $11
					.*									# possible whitespace or junk
                $							# End of line :-)
              !$7 - - [$3/$2/$1:$4 '$timezone'] \"GET /'$reqhost_1'/$5/$11/ HTTP/1.0" 200 0 "http://$8/" "" 0 $6-$9!x' \
		| ${perl} -pwe \
            's! 				# Deny inbound (No xlate) icmp
                ^							# Begin of line :-)
                    (\d+)\s(\w+)\s+(\w+)	\s+	# syslog year $1 month $2 day $3
                    ([0-9:]+)				\s+	# syslog time $4
                    [a-zA-Z0-9_.-]+			\s+	# syslog hostname
					\w\w\w \s+ \d+ \s+ \d+ \s+ [0-9:]+ \s # PIX date/time in UTC
					.+.Deny.inbound..No.xlate..icmp \s	# PIX ID (?); desc; protocol
					src \s (\w+):([0-9.]+)	\s	# Interface $5, Source IP $6
					dst \s (\w+):([0-9.]+)	\s	# Interface $7, Dest IP $8
					.type \s (\w+),.code .+		# ICMP type $9
                $							# End of line :-)
              !$6 - - [$3/$2/$1:$4 '$timezone'] \"GET /'$reqhost_2'/icmp/$9/ HTTP/1.0" 200 0 "http:///" "" 0 $5-$7!x' \
		| ${perl} -pwe \
            's! 				# translation creation failed for (tcp|udp)
                ^							# Begin of line :-)
                    (\d+)\s(\w+)\s+(\w+)	\s+	# syslog year $1 month $2 day $3
                    ([0-9:]+)				\s+	# syslog time $4
                    [a-zA-Z0-9_.-]+			\s+	# syslog hostname
					\w\w\w \s+ \d+ \s+ \d+ \s+ [0-9:]+ \s # PIX date/time in UTC
					.+translation.creation.failed.for.(udp|tcp) \s	# PIX ID (?); desc; protocol ($5)
					src \s (\w+):([0-9.]+)/([0-9]+)	\s	# Interface $6, Source IP $7, port $8
					dst \s (\w+):([0-9.]+)/([0-9]+)		# Interface $9, Dest IP $10, port $11
					.*									# possible whitespace or junk
                $							# End of line :-)
              !$7 - - [$3/$2/$1:$4 '$timezone'] \"GET /'$reqhost_1'/$5/$11/ HTTP/1.0" 200 0 "http://$8/" "" 0 $6-$9!x' \
		| ${perl} -pwe \
            's! 				# translation creation failed for icmp
                ^							# Begin of line :-)
                    (\d+)\s(\w+)\s+(\w+)	\s+	# syslog year $1 month $2 day $3
                    ([0-9:]+)				\s+	# syslog time $4
                    [a-zA-Z0-9_.-]+			\s+	# syslog hostname
					\w\w\w \s+ \d+ \s+ \d+ \s+ [0-9:]+ \s # PIX date/time in UTC
					.+translation.creation.failed.for.icmp \s	# PIX ID (?); desc; protocol
					src \s (\w+):([0-9.]+)	\s	# Interface $5, Source IP $6
					dst \s (\w+):([0-9.]+)	\s	# Interface $7, Dest IP $8
					.type \s (\w+),.code .+		# ICMP type $9
                $							# End of line :-)
              !$6 - - [$3/$2/$1:$4 '$timezone'] \"GET /'$reqhost_2'/icmp/$9/ HTTP/1.0" 200 0 "http:///" "" 0 $5-$7!x' \
		| ${perl} -pwe \
            's! 				# No translation group found for udp/tcp
                ^							# Begin of line :-)
                    (\d+)\s(\w+)\s+(\w+)	\s+	# syslog year $1 month $2 day $3
                    ([0-9:]+)				\s+	# syslog time $4
                    [a-zA-Z0-9_.-]+			\s+	# syslog hostname
					\w\w\w \s+ \d+ \s+ \d+ \s+ [0-9:]+ \s # PIX date/time in UTC
					.+No.translation.group.found.for.(udp|tcp) \s	# PIX ID (?); desc; protocol ($5)
					src \s (\w+):([0-9.]+)/([0-9]+)	\s	# Interface $6, Source IP $7, port $8
					dst \s (\w+):([0-9.]+)/([0-9]+)		# Interface $9, Dest IP $10, port $11
					.*									# possible whitespace or junk
                $							# End of line :-)
              !$7 - - [$3/$2/$1:$4 '$timezone'] \"GET /'$reqhost_1'/$5/$11/ HTTP/1.0" 200 0 "http://$8/" "" 0 $6-$9!x' \
		| ${perl} -pwe \
            's! 				# No translation group found for icmp
                ^							# Begin of line :-)
                    (\d+)\s(\w+)\s+(\w+)	\s+	# syslog year $1 month $2 day $3
                    ([0-9:]+)				\s+	# syslog time $4
                    [a-zA-Z0-9_.-]+			\s+	# syslog hostname
					\w\w\w \s+ \d+ \s+ \d+ \s+ [0-9:]+ \s # PIX date/time in UTC
					.+No.translation.group.found.for.icmp \s	# PIX ID (?); desc; protocol
					src \s (\w+):([0-9.]+)	\s	# Interface $5, Source IP $6
					dst \s (\w+):([0-9.]+)	\s	# Interface $7, Dest IP $8
					.type \s (\w+),.code .+		# ICMP type $9
                $							# End of line :-)
              !$6 - - [$3/$2/$1:$4 '$timezone'] \"GET /'$reqhost_2'/icmp/$9/ HTTP/1.0" 200 0 "http:///" "" 0 $5-$7!x' \
		> $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 ()
{
	# 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!"

	# change into /usr/local/etc/fwanalog
	cd /usr/local/etc/fwanalog
	
	# Load the user-settable options from the config file
	. `basename $0 | $sed 's/$/.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
	#####

	true
	# a no-op so the shell doesn't complain
}


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] [-a IP-addr] [-p packet]"
}


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)
	                  The sep_hosts and sep_packets commands in fwanalog.opts
	                  are ignored.
	-a IP-addr    Create a separate report for this host
	-p packet     Create a separate report for this packet 
	              Format: target/protocol/portnumber
				  e.g. 192.168.0.1/tcp/21 or firewall/udp/137
			
	NOTE: You must be the superuser to run this script, or have at least 
		  read rights to the firewall log. (See README.sudo for how to 
		  do this as a normal user.)
-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 ":hrta:p:" opt; do
	case $opt in
		r )	rotate_cache
			graceful_exit ;;

		h )	helptext
			graceful_exit ;;

		t )	today_only=true ;;

		a ) host_to_report="$OPTARG" ;;

		p ) packet_to_report="$OPTARG" ;;

		* )	usage
			exit 1
	esac
done

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

graceful_exit
