#!/bin/bash

cat <<EOTEXT1 >/dev/null
	(The above line allows me to put the documentation right in the
script... Cool, eh?)

>>>>>>>>>>>>>>>If you read nothing else, please read this<<<<<<<<<<<<<<<<

	This program offers an aid to creating firewall rules.  It offers
ABSOLUTELY NO intelligence in deciding what should be allowed or
disallowed.  It has ABSOLUTELY NO ability to understand your security
policy and implement it.  YOU are responsible for reviewing the rules and
massaging them to fit your needs.
	While the documentation in mason.txt attempts to provide some
general guidelines on how to use Mason, please remember:  the author has
no knowledge of what you want your firewall to do and has not tailored the
documentation or program to specially fit your needs.  If there is ever a
discrepancy between your needs and the program output or your needs and
the documentation, the program and/or documentation are _dead_ _wrong_. 


Copyleft:
	Mason interactively creates a Linux packet filtering firewall.
	Copyright (C) 1998 William Stearns <wstearns@pobox.com>

	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.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

	The author can also be reached at:
        William Stearns
email:  wstearns@pobox.com				(preferred)
web:    http://www.pobox.com/~wstearns
snail:  544 Winchester Place
        Colchester VT, 05446, USA


	This code is entirely owned by William Stearns
(wstearns@pobox.com) and has no relation to any employer or employer
sponsored project.  

------------------------------ Mason ------------------------------
	The Mason script interactively builds a (fire)wall on a Linux
machine. For more details about how this is done, please see mason.txt,
which gives background, theory of operation, a quick start, and additional
documentation on firewalls and firewall gotchas. 
	mason.txt and related documentation should have been installed to
/usr/doc/mason-{version}/ .  If they are missing or you would like to make
sure you have the latest version, please go to
http://www.pobox.com/~wstearns/mason/ . 


- Bill Stearns

The EOTEXT line is the end of the text and the start of the code. 
EOTEXT1


#-------------------------------------------------------------------------
# Set any parameters that have not already been set
# Probably not a good idea to edit below this line...
#-------------------------------------------------------------------------

#This is the configuration file mason uses.  The parameters in it can be 
#changed while Mason is running as long as the SIGUSR1 signal is sent to 
#Mason afterwards.  This can be done by typing "killall -USR1 mason"
  MASONCONF=${MASONCONF:-"/var/lib/mason/masonrc"}

    MASONDIR=${MASONDIR:-"/var/lib/mason/"}
  NAMECACHE=${NAMECACHE:-"/var/lib/mason/morehosts"}
    NETCACHE=${NETCACHE:-"/var/lib/mason/netconvert"}
POLICYFILE=${POLICYFILE:-"/var/lib/mason/current_policy"}

NOLOGSUFFIX=${NOLOGSUFFIX:-"N"}	#If you change this, change it in ruleshell too.
DEBUG=${DEBUG:-"NO"}

#What to use if there is no policyfile or its contents are not accept, 
#reject, or deny.  Please, no typos...
DEFAULTPOLICY="accept"

case ${ECHOCOMMAND} in
ipfwadm|IPFWADM)	ECHOCOMMAND="ipfwadm"	;;
ipchains|IPCHAINS)	ECHOCOMMAND="ipchains"	;;
no|NO|none|NONE)	ECHOCOMMAND="none"		;;
*)
	if [ -f /proc/net/ip_fwchains ]; then
		ECHOCOMMAND="ipchains"
	elif [ -f /proc/net/ip_input ]; then
		ECHOCOMMAND="ipfwadm"
	else
		ECHOCOMMAND="ipchains"			#Set default here
	fi
	;;
esac


case ${DOCOMMAND} in
ipfwadm|IPFWADM)	DOCOMMAND="ipfwadm"		;;
ipchains|IPCHAINS)	DOCOMMAND="ipchains"	;;
no|NO|none|NONE)	DOCOMMAND="none"		;;
*)
	if [ -f /proc/net/ip_fwchains ]; then
		DOCOMMAND="ipchains"
	elif [ -f /proc/net/ip_input ]; then
		DOCOMMAND="ipfwadm"
	else
		DOCOMMAND="none"				#Well, we can't _do_ either!
	fi
	;;
esac

case ${HEARTBEAT} in
yes|YES)			HEARTBEAT="yes"	;;
no|NO|none|NONE)	HEARTBEAT="no"	;;
*)					HEARTBEAT="yes"	;;
esac

case ${IPCONV} in
hostname|HOSTNAME)	IPCONV="hostname"	;;
network|NETWORK)	IPCONV="network"	;;
no|NO|none|NONE)	IPCONV="none"		;;
#custom|CUSTOM)		IPCONV="custom"		;;
*)					IPCONV="network"	;;
esac

#Beeps to be added later
DOBEEP=${DOBEEP:-"YES"}	# "YES" = beep at user with new rule, "NO" = dont

LOWSSHPORT=${STARTLOWSSHPORT:-"1010"}



#-------------------------------------------------------------------------
# Start setting up.
#-------------------------------------------------------------------------

if [ -w /etc/services ]; then
	if [ `cat /etc/services | grep '22/tcp' | wc -l` -eq 0 ]; then
		echo -e "ssh\t22/tcp\t\t# Secure shell (added by Mason)" >>/etc/services
	fi
	if [ `cat /etc/services | grep '111/udp' | wc -l` -eq 0 ]; then
		echo -e "sunrpc\t111/udp\tportmapper\t# RPC 4.0 portmapper udp (added by Mason)" >>/etc/services
	fi
	if [ `cat /etc/services | grep '111/tcp' | wc -l` -eq 0 ]; then
		echo -e "sunrpc\t111/tcp\tportmapper\t# RPC 4.0 portmapper tcp (added by Mason)" >>/etc/services
	fi
	#if [ `cat /etc/services | grep '635/tcp' | wc -l` -eq 0 ]; then
	#	echo -e "mount\t635/tcp\t\t# NFS Mount Service ?? (added by Mason)" >>/etc/services
	#fi
	if [ `cat /etc/services | grep '635/udp' | wc -l` -eq 0 ]; then
		echo -e "mount\t635/udp\t\t# NFS Mount Service (added by Mason)" >>/etc/services
	fi
	if [ `cat /etc/services | grep '2049/tcp' | wc -l` -eq 0 ]; then
		echo -e "nfs\t2049/tcp\t\t# NFS File Service (added by Mason)" >>/etc/services
	fi
	if [ `cat /etc/services | grep '2049/udp' | wc -l` -eq 0 ]; then
		echo -e "nfs\t2049/udp\t\t# NFS File Service (added by Mason)" >>/etc/services
	fi
else
	echo /etc/services is not writable by this user.  Please make sure >/dev/stderr
	echo the following ports are defined in /etc/services: >/dev/stderr
	echo -e "ssh\t22/tcp\t\t# Secure shell" >/dev/stderr
	echo -e "sunrpc\t111/udp\tportmapper\t# RPC 4.0 portmapper udp" >/dev/stderr
	echo -e "sunrpc\t111/tcp\tportmapper\t# RPC 4.0 portmapper tcp" >/dev/stderr
	echo -e "mount\t635/tcp\t\t# NFS Mount Service ??" >/dev/stderr
	echo -e "mount\t635/udp\t\t# NFS Mount Service" >/dev/stderr
	echo -e "nfs\t2049/tcp\t\t# NFS File Service" >/dev/stderr
	echo -e "nfs\t2049/udp\t\t# NFS File Service" >/dev/stderr
	echo >/dev/stderr
fi

LOGCHAINSEXIST="yes"
ALLCHAINS="`/sbin/ipchains -L -n | grep '^Chain ' | awk '{print $2}'`"

if [ "${DOCOMMAND}" = "ipchains" ]; then
	if [ -n "${NOLOGSUFFIX}" ]; then
		for ONECHAIN in ${ALLCHAINS} ; do
			case ${ONECHAIN} in
			*${NOLOGSUFFIX})	:								;;
			*)	# For each chain (except for the nolog chains themselves) check that a corresponding nolog chain exists.
				#Do not use -n.
				if ! /sbin/ipchains -L ${ONECHAIN}${NOLOGSUFFIX} >/dev/null 2>/dev/null ; then  #If nolog chain does not exist
					LOGCHAINSEXIST="no"
					echo The ${ONECHAIN} chain exists without a corresponding ${ONECHAIN}${NOLOGSUFFIX} >/dev/stderr
					echo chain.  For the moment, the "nolog" feature will revert to sticking the rules >/dev/stderr
					echo at the top of the original chain. >/dev/stderr
				fi												;;
			esac
		done
		if [ "${LOGCHAINSEXIST}" = "no" ]; then ${NOLOGSUFFIX} = "" ; fi
	fi
fi


LAST1="" ; LAST2="" ; LAST3="" ; LAST4="" ; LAST5="" ; CURRENT=""
EXITMASON=${EXITMASON:-"NO"}

if [ ! -d ${MASONDIR} ]; then mkdir --parents ${MASONDIR} ; chown root.root ${MASONDIR} ; chmod 700 ${MASONDIR} ; fi
if [ ! -f ${MASONCONF} ]; then touch ${MASONCONF} ; chmod 700 ${MASONCONF} ; fi

. ${MASONCONF}

sigexitscript () {
	if [ "${NEEDLF}" = "YES" ]; then echo >/dev/stderr ; NEEDLF="NO" ; fi
	echo Received HUP signal, exiting at next pass >/dev/stderr
	EXITMASON="YES"
}
trap sigexitscript SIGHUP

sigreloadconf () {
	if [ "${NEEDLF}" = "YES" ]; then echo >/dev/stderr ; NEEDLF="NO" ; fi
	echo Received USR1 signal >/dev/stderr
	if [ -f ${MASONCONF} ]; then
		echo loading new options from ${MASONCONF} >/dev/stderr
		. ${MASONCONF}
	else
		echo Unable to load new options, ${MASONCONF} does not exist. >/dev/stderr
	fi
}
trap sigreloadconf SIGUSR1


classifyips () {
	ALLIPS="`ifconfig | grep 'inet addr' | sed -e 's/.*addr://' -e 's/ .*//'` \
	`route -n | grep '^[0-9\.]* *[0-9\.]* *255\.255\.255\.255' | awk '{print $1}'`"
	ALLBCS=`ifconfig | grep 'Bcast' | sed -e 's/.*Bcast://' -e 's/ .*//'`
}


classifynets () {
#This detects all the networks in the current routing table.
	rm -f ${NETCACHE}
	touch ${NETCACHE}
	chmod 700 ${NETCACHE}
	for ONENET in `route -n | grep -v '^127\.' | grep -v '^0\.0\.0\.0' | grep '^[0-9]' | awk '{print $1 "/" $3}'` ; do
		case ${ONENET} in
		*/255.255.255.255)	:		;;
		*/255.255.255.254)	:		;;
		*/255.255.255.252)	:		;;
		*/0.0.0.0)			:		;;
		*)
			eval `ipcalc --broadcast ${ONENET%%/*} ${ONENET##*/}`
			echo ${ONENET%%/*}-${BROADCAST}/${ONENET##*/} >>${NETCACHE} #network-broadcast/netmask
			;;
		esac
	done
}

#Not currently used...
ipeq () {
	SPLITIP=${1}
	unset I1O1 I1O2 I1O3 I1O4
	I1O1=${SPLITIP%%.*} ; SPLITIP=${SPLITIP#*.}
	I1O2=${SPLITIP%%.*} ; SPLITIP=${SPLITIP#*.}
	I1O3=${SPLITIP%%.*} ; SPLITIP=${SPLITIP#*.}
	I1O4=${SPLITIP}
	SPLITIP=${2}
	unset I2O1 I2O2 I2O3 I2O4
	I2O1=${SPLITIP%%.*} ; SPLITIP=${SPLITIP#*.}
	I2O2=${SPLITIP%%.*} ; SPLITIP=${SPLITIP#*.}
	I2O3=${SPLITIP%%.*} ; SPLITIP=${SPLITIP#*.}
	I2O4=${SPLITIP}

	if [ $I1O1 -eq $I2O1 -a $I1O2 -eq $I2O2 -a $I1O3 -eq $I2O3 -a $I1O4 -eq $I2O4 ]; then
		return 0 #True
	else
		return 1 #False
	fi
}

iple () {
#Returns true (0) if IP1 is less than or equal to IP2, else false (1)
#if iple 128.2.3.4 127.0.0.1 ; then echo less than or eq ; else echo ge ; fi
	SPLITIP=${1}
	unset I1O1 I1O2 I1O3 I1O4
	I1O1=${SPLITIP%%.*} ; SPLITIP=${SPLITIP#*.}
	I1O2=${SPLITIP%%.*} ; SPLITIP=${SPLITIP#*.}
	I1O3=${SPLITIP%%.*} ; SPLITIP=${SPLITIP#*.}
	I1O4=${SPLITIP}
	SPLITIP=${2}
	unset I2O1 I2O2 I2O3 I2O4
	I2O1=${SPLITIP%%.*} ; SPLITIP=${SPLITIP#*.}
	I2O2=${SPLITIP%%.*} ; SPLITIP=${SPLITIP#*.}
	I2O3=${SPLITIP%%.*} ; SPLITIP=${SPLITIP#*.}
	I2O4=${SPLITIP}

	  if [ $I1O1 -lt $I2O1 ]; then	return 0
	elif [ $I1O1 -gt $I2O1 ]; then	return 1
	elif [ $I1O2 -lt $I2O2 ]; then	return 0
	elif [ $I1O2 -gt $I2O2 ]; then	return 1
	elif [ $I1O3 -lt $I2O3 ]; then	return 0
	elif [ $I1O3 -gt $I2O3 ]; then	return 1
	elif [ $I1O4 -lt $I2O4 ]; then	return 0
	elif [ $I1O4 -gt $I2O4 ]; then	return 1
	else							return 0
	fi
}

ipval () {
	#Damn.  Bash shell variables are _signed_.
	#Need to rethink this.
	SPLITIP=${1}
	unset OCT1 OCT2 OCT3 OCT4
	OCT1=${SPLITIP%%.*} ; SPLITIP=${SPLITIP#*.}
	OCT2=${SPLITIP%%.*} ; SPLITIP=${SPLITIP#*.}
	OCT3=${SPLITIP%%.*} ; SPLITIP=${SPLITIP#*.}
	OCT4=${SPLITIP}
	#echo $OCT1 $OCT2 $OCT3 $OCT4
	RETVAL=$[ ((((($OCT1*256)+OCT2)*256)+$OCT3)*256)+$OCT4 ]
	echo ${RETVAL}
}
#if [ `ipval 1.2.3.4` -ge 20000000 ]; then echo bigger than 20000000 ; fi
#echo `ipval 1.0.0.0`
#echo `ipval 4.3.2.1`
#BOTTOM=172.16.0.2
#TOP=172.16.255.254
#TEST=0.0.0.0
#echo `ipval ${TEST}`, `ipval ${BOTTOM}`-`ipval ${TOP}`
#if [ `ipval ${TEST}` -ge `ipval ${BOTTOM}` ] && [ `ipval ${TEST}` -le `ipval ${TOP}` ]; then
#	echo in range
#elif [ `ipval ${TEST}` -lt `ipval ${BOTTOM}` ]; then
#	echo under range
#else
#	echo over range
#fi

convmasktobits () {
	case ${1} in
	255.255.255.255)	NUMBITS=32 ;;
	255.255.255.254)	NUMBITS=31 ;;
	255.255.255.252)	NUMBITS=30 ;;
	255.255.255.248)	NUMBITS=29 ;;
	255.255.255.240)	NUMBITS=28 ;;
	255.255.255.224)	NUMBITS=27 ;;
	255.255.255.192)	NUMBITS=26 ;;
	255.255.255.128)	NUMBITS=25 ;;
	255.255.255.0)		NUMBITS=24 ;;
	255.255.254.0)		NUMBITS=23 ;;
	255.255.252.0)		NUMBITS=22 ;;
	255.255.248.0)		NUMBITS=21 ;;
	255.255.240.0)		NUMBITS=20 ;;
	255.255.224.0)		NUMBITS=19 ;;
	255.255.192.0)		NUMBITS=18 ;;
	255.255.128.0)		NUMBITS=17 ;;
	255.255.0.0)		NUMBITS=16 ;;
	255.254.0.0)		NUMBITS=15 ;;
	255.252.0.0)		NUMBITS=14 ;;
	255.248.0.0)		NUMBITS=13 ;;
	255.240.0.0)		NUMBITS=12 ;;
	255.224.0.0)		NUMBITS=11 ;;
	255.192.0.0)		NUMBITS=10 ;;
	255.128.0.0)		NUMBITS=9 ;;
	255.0.0.0)			NUMBITS=8 ;;
	254.0.0.0)			NUMBITS=7 ;;
	252.0.0.0)			NUMBITS=6 ;;
	248.0.0.0)			NUMBITS=5 ;;
	240.0.0.0)			NUMBITS=4 ;;
	224.0.0.0)			NUMBITS=3 ;;
	192.0.0.0)			NUMBITS=2 ;;
	128.0.0.0)			NUMBITS=1 ;;
	0.0.0.0)			NUMBITS=0 ;;
	esac
}


clientportrange () {
	CLIENTPORT=${1} ; SERVERPORT=${2} ; PRPROTO=${3}
	RETVAL="1024:65535"

	if [ ${CLIENTPORT} -ge $PORT_MASQ_BEGIN ] && [ ${CLIENTPORT} -le ${PORT_MASQ_END} ]; then
		RETVAL="${PORT_MASQ_BEGIN}:${PORT_MASQ_END}"
	elif [ ${CLIENTPORT} -ge 33434 ] && [ ${CLIENTPORT} -le 33463 ] && [ "${PRPROTO}" = "udp" ]; then
		RETVAL="33434:33463"		# Should this be higher?
	else
		case "${SERVERPORT}/${PRPROTO}" in
		"22/tcp")
			if [ ${CLIENTPORT} -le 1023 ]; then
				while [ "${CLIENTPORT}" -lt "${LOWSSHPORT}" ]; do
					LOWSSHPORT=`expr ${LOWSSHPORT} - 10`
				done
				RETVAL="${LOWSSHPORT}:1023"
			fi																				;;
		"635/tcp"|"111/udp"|"2049/udp"|"635/udp")
			if [ ${CLIENTPORT} -le 1023 ]; then RETVAL="0:1023" ; fi						;;
		*)
			if [ ${CLIENTPORT} -le 1023 ]; then RETVAL="${CLIENTPORT}" ; fi					;;
		esac
	fi
	echo ${RETVAL}
}

nameof () {
	NAMEOFRETVAL=${1}
	ONEHOSTNAME=`cat ${NAMECACHE} /etc/hosts | egrep "^${1}[^0-9]" | tail --lines=1 | awk '{print $2}'`
	if [ -n "${ONEHOSTNAME}" ]; then
		NAMEOFRETVAL="${ONEHOSTNAME}"
	elif [ "${SRCPORT}" != "53" -a "${DESTPORT}" != "53" ]; then
#FIXME - split pattern matching onto its own line and do with bash?
		if host -t ptr ${1} >/dev/null 2>/dev/null ; then
			ONEHOSTNAME=`host -t ptr ${1} 2>/dev/null | grep 'domain name pointer' | head --lines=1 | sed -e 's/.* //'`
			if [ -n "${ONEHOSTNAME}" ]; then
				echo -e "${1}\t${ONEHOSTNAME}" >>${NAMECACHE}
				NAMEOFRETVAL="${ONEHOSTNAME}"
			fi
		fi
	fi
	echo ${NAMEOFRETVAL}
}


generalizeip () {
#Use the namecache file, /etc/hosts, and finally "host" command to look
#up names for source and destination addresses.
	RETVAL="${1}/32"
	ISASSIGNED="NO"
	for ONEIF in ${DYNIF} ; do
		DYNIP=$(eval echo \${$(eval echo ${ONEIF}ADDR)})	#No, really.
		if [ "${1}" = "${DYNIP}" ]; then
			RETVAL="\${${ONEIF}ADDR}"
			ISASSIGNED="YES"
			LINEHASDYNAMIC="YES"
		fi
	done

	if [ "${ISASSIGNED}" = "NO" ]; then
		case ${IPCONV} in
		hostname)
			RETVAL="`nameof ${1}`/32"
#Handled by above statement
#			ONEHOSTNAME=`cat ${NAMECACHE} /etc/hosts | egrep "^${1}[^0-9]" | tail --lines=1 | awk '{print $2}'`
#			if [ -n "${ONEHOSTNAME}" ]; then
#				RETVAL="${ONEHOSTNAME}/32"
#			elif [ "${SRCPORT}" != "53" -a "${DESTPORT}" != "53" ]; then
##FIXME - split pattern matching onto its own line and do with bash?
#				if host -t ptr ${1} >/dev/null 2>/dev/null ; then
#					ONEHOSTNAME=`host -t ptr ${1} 2>/dev/null | grep 'domain name pointer' | head --lines=1 | sed -e 's/.* //'`
#					if [ -n "${ONEHOSTNAME}" ]; then
#						echo -e "${1}\t${ONEHOSTNAME}" >>${NAMECACHE}
#						RETVAL="${ONEHOSTNAME}/32"
#					fi
#				fi
#			fi
#
			;;
		network)
#Handle special addresses
			case ${1} in
			0.0.0.0)			RETVAL="0.0.0.0/32"			ISASSIGNED="YES"	;;
			127.0.0.1)			RETVAL="localhost/32"		ISASSIGNED="YES"	;;
			255.255.255.255)	RETVAL="255.255.255.255/32"	ISASSIGNED="YES"	;;
			esac

#Leave local IP addresses and broadcasts as they are.
			if [ "${ISASSIGNED}" = "NO" ]; then
				for ONELOCALIP in ${ALLIPS} ${ALLBCS} ; do
					#echo ${1} = ${ONELOCALIP} ? >/dev/stderr
					if [ "${1}" = "${ONELOCALIP}" ]; then ISASSIGNED="YES" ; fi
				done
			fi

#If IP is in a local netblock, generalize to that netblock.
			if [ "${ISASSIGNED}" = "NO" ]; then
				for ONENET in `cat ${NETCACHE}` ; do
					if [ "${ISASSIGNED}" = "NO" ]; then
						NETMASK=${ONENET##*/}	; ONENET=${ONENET%/*}
						BROADCAST=${ONENET##*-}	; ONENET=${ONENET%-*}
						if iple ${ONENET} ${1} ; then
							if iple ${1} ${BROADCAST} ; then
								convmasktobits ${NETMASK}
								RETVAL="${ONENET}/${NUMBITS}"
								ISASSIGNED="YES"
								#echo assigning ${RETVAL}
							fi
						fi
					fi
				done
			fi

			if [ "${ISASSIGNED}" = "NO" ]; then		RETVAL="0/0" ; ISASSIGNED="YES" ; fi
			;;
		none)													;;
		#custom)												;;
		esac
	fi
#FIXME - return as stdout
	#Hmmm... can't return a non-numeric arg, so I'll nab it as a global variable. 
	#return "${RETVAL}"
}

#---------- End of functions ----------


#---------- Start of Main code ----------
classifyips
classifynets

#Hmmm... I _hate_ overwriting /etc/passwd...
if [ -L ${NAMECACHE} ]; then
	rm -f ${NAMECACHE}
fi

#Create the name cache file if it doesn't exist
if [ ! -e ${NAMECACHE} ]; then
	touch ${NAMECACHE}
	#This is a security-related program.  I don't want to let people know who we're even talking to.
	chmod og-rwx ${NAMECACHE}
fi

#FIXME - get the commented 0/0 up at the top.
for ONEIF in ${DYNIF} ; do
	cat <<EOTEXT
#export ${ONEIF}ADDR="0/0"		#Use this version if you want to match any IP address.
export ${ONEIF}ADDR="\`ifconfig ${ONEIF} | grep 'inet addr' | awk '{print \$2}' | sed -e 's/.*://'\`/32"
EOTEXT
	eval ${ONEIF}ADDR=`ifconfig ${ONEIF} | grep 'inet addr' | awk '{print $2}' | sed -e 's/.*://'`
done


#Get the first log entry
unset ACK COMMENT DEST DFFLAG DESTHOST DESTIP DESTPORT DIR DIRLETTER \
DOACK ECHOACK FFLAG FOFLAG IF IFLAG ISLOGLINE \
J1 J2 J3 J4 J5 J6 J7 J8 J9 J10 J11 J12 J13 J14 J15 J16 J17 J18 J19 \
                  O7 O8 O9 O10 O11 O12 O13 \
                  A7 A8 A9 A10 A11 A12 A13 \
LINEHASDYNAMIC LFLAG MESSPOL POLICY PROTO SFLAG SRC SRCHOST SRCIP SRCPORT TAIL TFLAG
#read J1 J2 J3 J4 J5 J6 DIR MESSPOL IF PROTO SRC DEST \
#     LFLAG SFLAG IFLAG FOFLAG TFLAG DFFLAG TAIL
 read J1 J2 J3 J4 J5 J6 J7  J8      J9 J10   J11 J12  \
      J13   J14   J15   J16    J17   J18    J19

#FIXME invalid log entries are getting through - or syslog is screwing up.  Which?
while [ ! "${EXITMASON}" = "YES" ] && [ -n "${J1}" ]; do
#Debug statements
#Only do the work if the line is a firewalling log entry.
	if [ "${J5} ${J6}" = "kernel: IP" ] && [ "`echo ${J7} | cut -b 1-3`" = "fw-" ]; then
#read J1 J2 J3 J4 J5 J6 DIR MESSPOL IF PROTO SRC DEST \
#     LFLAG SFLAG IFLAG FOFLAG TFLAG DFFLAG TAIL
#read J1 J2 J3 J4 J5 J6 J7  J8      J9 J10   J11 J12  \
#     J13   J14   J15   J16    J17   J18    J19
		case ${J7} in
		fw-out)	DIR='output ' ;	DIRLETTER='O'					;;
		fw-in)	DIR='input  ' ;	DIRLETTER='I'					;;
		fw-fwd)	DIR='forward' ;	DIRLETTER='F'					;;
		*)	echo Unknown direction X${J7}X >/dev/stderr			;;
		esac
		MESSPOL="${J8}" ; IF="${J9}"
		PROTO=`echo ${J10} | tr A-Z a-z`
		SRC="${J11}" ; DEST="${J12}"
		LFLAG="${J13}" ; SFLAG="${J14}" ; IFLAG="${J15}" ; FOFLAG="${J16}" ; TFLAG="${J17}" ; DFFLAG="${J18}"
#Break up ipfwadmin's habit of gluing the icmp port onto the protocol.
		if [ "${PROTO%%/*}" = "icmp" ]; then
			SRCPORT=${PROTO##*/}
			PROTO="icmp"
			DESTPORT=""
			if [ "${DEBUG}" = "YES" ]; then echo proto= ${PROTO} srcport= ${SRCPORT} destport= ${DESTPORT} ; fi
		else
			SRCPORT=${SRC##*:}
			DESTPORT=${DEST##*:}
		fi
		ISLOGLINE="YES"
	fi
	if [ "${J6} ${J7}" = "Packet log:" ]; then
		case ${J8} in
		output)		DIR='output ' ;	DIRLETTER='O'				;;
		input)		DIR='input  ' ;	DIRLETTER='I'				;;
		forward)	DIR='forward' ;	DIRLETTER='F'				;;
		*)	echo Unknown direction X${J8}X >/dev/stderr			;;
		esac
		MESSPOL="${J9}" ; IF="${J10}" ; 
		case "${J11}" in
		"PROTO=0")		PROTO="ip"								;;
		"PROTO=1")		PROTO="icmp"							;;
		"PROTO=2")		PROTO="igmp"							;;
		"PROTO=3")		PROTO="ggp"								;;
		"PROTO=6")		PROTO="tcp"								;;
		"PROTO=12")		PROTO="pup"								;;
		"PROTO=17")		PROTO="udp"								;;
		"PROTO=22")		PROTO="idp"								;;
		"PROTO=255")	PROTO="raw"								;;
		*)		echo Unknown protocol X${J11}X >/dev/stderr		;;
		esac
		SRC="${J12}" ; DEST="${J13}" ; 
		SRCPORT=${SRC##*:}
		DESTPORT=${DEST##*:}
		LFLAG="${J14}" ; SFLAG="${J15}" ; IFLAG="${J16}" ; FFLAG="${J17}" ; TFLAG="${J18}"
		ISLOGLINE="YES"
	fi	

	if [ "${ISLOGLINE}" = "YES" ]; then
		SRCIP=${SRC%%:*}
		DESTIP=${DEST%%:*}
		ACK="  "

		if [ ${#IF} -lt 5 ]; then
			IF=`echo "${IF}     " | cut -b 1-4`
		fi

#Use the namecache file, /etc/hosts, and finally "host" command to look
#up names for source and destination addresses.

		generalizeip ${SRCIP}
		SRCHOST=${RETVAL}

		generalizeip ${DESTIP}
		DESTHOST=${RETVAL}

		COMMENT2=""
#Clean up protocol type and number fields, visualize source and dest port fields, set ack flag.
		if [ "${PROTO}" != "icmp" ]; then
#If port not in /etc/services and >=1024, generalize to "high port"
#clientportrange (CLIENTPORT, SERVERPORT, PROTO)
		    SOURCESERVICE=`cat /etc/services | grep "${SRCPORT}/${PROTO}" | head -n 1`
		    DESTSERVICE=`cat /etc/services | grep "${DESTPORT}/${PROTO}" | head -n 1`
		    if [ -n "${SOURCESERVICE}" ] && [ "${PROTO}" = "tcp" ]; then
				#The ack flag should be set if port=tcp and source port is a server service.
				if [ "${SRCPORT}/${PROTO}" != "20/tcp" ]; then ACK="-k" ; fi
			fi
			if [ -n "${SOURCESERVICE}" ] && [ -n "${DESTSERVICE}" ]; then
				# Both source and destination ports are servers.
		        SRCPORT=`echo ${SOURCESERVICE} | awk '{print $1}'`
		        DESTPORT=`echo ${DESTSERVICE} | awk '{print $1}'`
			elif [ -n "${SOURCESERVICE}" ]; then
				# Source port is a server port.
		        SRCPORT=`echo ${SOURCESERVICE} | awk '{print $1}'`
				DESTPORT=`clientportrange ${DESTPORT} ${SRCPORT} ${PROTO}`
			elif [ -n "${DESTSERVICE}" ]; then
				# Dest port is a server port.
		        DESTPORT=`echo ${DESTSERVICE} | awk '{print $1}'`
				SRCPORT=`clientportrange ${SRCPORT} ${DESTPORT} ${PROTO}`
			else
				# Neither source nor dest is a server port.
#FIXME - return hostname is available, ip if not.
				COMMENT2="## S=`nameof ${SRCIP}`:${SRCPORT} D=`nameof ${DESTIP}`:${DESTPORT}"
				SRCPORT=`clientportrange ${SRCPORT} ${DESTPORT} ${PROTO}`
				DESTPORT=`clientportrange ${DESTPORT} ${SRCPORT} ${PROTO}`
			fi
		fi

#If we have a high port to high port connection (darn ftp), do _not_ generalize to anywhere
#FIXME - if we're reverting to a single IP (and this is obviously not a dns lookup), replace with host_name_.
		if [ "${SRCPORT}" = "1024:65535" ] && [ "${DESTPORT}" = "1024:65535" ]; then
			if [ "${SRCHOST}" = "0/0" ]; then	SRCHOST="${SRCIP}/32" ;		fi
			if [ "${DESTHOST}" = "0/0" ]; then	DESTHOST="${DESTIP}/32" ;	fi
		fi

#Set comment
		if [ "${PROTO%%/*}" = "icmp" ]; then
		    case ${SRCPORT} in
		    0)		COMMENT="# Echo reply/icmp(${DIRLETTER})"			;;
		    3)		COMMENT="# Dest Unreach/icmp(${DIRLETTER})"			;;
		    4)		COMMENT="# Source Quench/icmp(${DIRLETTER})"		;;
		    5)		COMMENT="# Redirect/icmp(${DIRLETTER})"				;;
		    8)		COMMENT="# Echo req/icmp(${DIRLETTER})"				;;
		    11)		COMMENT="# Time exceeded/icmp(${DIRLETTER})"		;;
		    12)		COMMENT="# Parameter prob/icmp(${DIRLETTER})"		;;
		    13)		COMMENT="# Timestamp req/icmp(${DIRLETTER})"		;;
		    14)		COMMENT="# Timestamp reply/icmp(${DIRLETTER})"		;;
		    15)		COMMENT="# Info req/icmp(${DIRLETTER})"				;;
		    16)		COMMENT="# Info reply/icmp(${DIRLETTER})"			;;
		    17)		COMMENT="# Addr Mask req/icmp(${DIRLETTER})"		;;
		    18)		COMMENT="# Addr Mask reply/icmp(${DIRLETTER})"		;;
		    *)		COMMENT="# unknown-${SRCPORT}/icmp(${DIRLETTER})"	;;
		    esac
		else
		    COMMENT="#"
			if [ "${SRCPORT}/${PROTO}"  = "22/tcp" ] || [ "${SRCPORT}/${PROTO}"  = "ssh/tcp" ] || \
			   [ "${DESTPORT}/${PROTO}" = "22/tcp" ] || [ "${DESTPORT}/${PROTO}" = "ssh/tcp" ]; then
			    COMMENT="${COMMENT} ssh/tcp"
			else
#If port not in /etc/services and >=1024, generalize to "high port"
			    if [ "${SRCPORT}" != "1024:65535" ]; then COMMENT="${COMMENT} ${SRCPORT}/${PROTO}" ; fi

			    if [ "${DESTPORT}" != "1024:65535" ]; then
			        if [ "${SRCPORT}" != "${DESTPORT}" ]; then COMMENT="${COMMENT} ${DESTPORT}/${PROTO}" ; fi
			    fi
			fi
		    COMMENT="${COMMENT} (${DIRLETTER})"
		fi
		if [ "${LINEHASDYNAMIC}" = "YES" ]; then COMMENT="${COMMENT} DynamicIP" ; fi

#Figure out what policy to use.  This is checked at every rule so user can 
#change the policy on the fly.
		if [ -f ${POLICYFILE} ]; then
		    POLICY=`cat ${POLICYFILE} | tr A-Z a-z | head --lines=1 | sed -e 's/[^acdejnprty]//g'`
		    case ${POLICY} in
		    accept|reject|deny)	:								;;
		    *)			POLICY=${DEFAULTPOLICY}					;;
		    esac
		else
		    POLICY=${DEFAULTPOLICY}
		fi
		if [ "${POLICY}" = "deny" ]; then POLICY="deny  " ; fi
	
	    if [ "${DEBUG}" = "YES" ]; then
			echo J1=${J1}, J2=${J2}, J3=${J3}, J4=${J4}
			echo J5=${J5}, J6=${J6}, J7=${J7}, J8=${J8}, 
			echo J9=${J9}, J10=${J10}, J11=${J11}, J12=${J12},
			echo J13=${J13}, J14=${J14}, J15=${J15}, J16=${J16},
			echo J17=${J17}, J18=${J18}, J19=${J19},
			echo DIR=${DIR}, DIRLETTER=${DIRLETTER}
			echo MESSPOL=${MESSPOL}, IF=${IF}, PROTO=${PROTO}
			echo SRC=${SRC}, DEST=${DEST}, LFLAG=${LFLAG}
			echo SFLAG=${SFLAG}, IFLAG=${IFLAG}, FOFLAG=${FOFLAG}, FFLAG=${FFLAG}
			echo TFLAG=${TFLAG}, DFFLAG=${DFFLAG}, TAIL=${TAIL}
	    fi


#Actually create and implement the firewall command to display.
#Pad so rules line up.
		case ${PROTO} in
		ip|IP)												PROTO="${PROTO}  "	;;
		tcp|TCP|udp|UDP|ggp|GGP|pup|PUP|idp|IDP|raw|RAW)	PROTO="${PROTO} "	;;
		esac
		if [ "${ECHOCOMMAND}" = "ipchains" ]; then
			if [ "${ACK}" = "  " ]; then ECHOACK="    " ;  else ECHOACK="! -y" ; fi
			POLICY=`echo ${POLICY} | tr a-z A-Z`
			CURRENT="/sbin/ipchains -A ${DIR} -i ${IF} -p ${PROTO} ${ECHOACK} -s ${SRCHOST} ${SRCPORT} -d ${DESTHOST} ${DESTPORT} -j ${POLICY}"
		elif [ "${ECHOCOMMAND}" = "ipfwadm" ]; then
			POLICY=`echo ${POLICY} | tr A-Z a-z`
			CURRENT="/sbin/ipfwadm -a ${POLICY} -W ${IF} -${DIRLETTER} -P ${PROTO} ${ACK} -S ${SRCHOST} ${SRCPORT} -D ${DESTHOST} ${DESTPORT}"
		fi # No need to handle ECHOCOMMAND=none :-)
		if [ `echo -n ${CURRENT} | wc -c` -lt 111 ]; then	#We cannot use ${#CURRENT} because it counts multiple spaces as one.
		    CURRENT=`echo "${CURRENT}                                                   " | cut -b 1-120`	#orig 110 chopped lines
		fi
		CURRENT="${CURRENT} ${COMMENT}"

		if [ "${DEBUG}" = "YES" ]; then echo current= ${CURRENT} ; fi

#Don't do anything if this is the same as one of the last 5 rules.  This 
#reduces the occurence of repeated rules showing up.
		case ${CURRENT} in
		${LAST1}|${LAST2}|${LAST3}|${LAST4}|${LAST5})
			if [ "${HEARTBEAT}" = "yes" ]; then echo -n "." >/dev/stderr ; NEEDLF="YES" ; fi
			;;
		*)
			if [ "${HEARTBEAT}" = "yes" ]; then
				if [ "${NEEDLF}" = "YES" ]; then echo >/dev/stderr ; NEEDLF="NO" ; fi
			fi
		    #Beeps later...
		    #if [ "${DOBEEP}" = "YES" ]; then echo -e "\a" ; fi
			case ${ECHOCOMMAND} in
			ipchains|ipfwadm)	echo "${CURRENT} ${COMMENT2}"				;;
			esac

#Put the dynamic IP addresses in the current environment.
			for ONEIF in ${DYNIF} ; do
				DYNIP=$(eval echo \${$(eval echo ${ONEIF}ADDR)})	#No, really.
				eval ${ONEIF}ADDR=${DYNIP}
			done

#Put a real rule in the rule chain so that we don't log it again.  We need to use eval since ${xxxHOST} may be ${DYNIP} 
#and may need to be evaluated to its real value.
			if [ "${DOCOMMAND}" = "ipfwadm" ]; then 
				POLICY=`echo ${POLICY} | tr A-Z a-z`
				eval "/sbin/ipfwadm -i ${POLICY} -W ${IF} -${DIRLETTER} -P ${PROTO} ${ACK} -S ${SRCHOST} ${SRCPORT} -D ${DESTHOST} ${DESTPORT}"
			elif [ "${DOCOMMAND}" = "ipchains" ]; then
				if [ "${ACK}" = "  " ]; then DOACK="    " ; else DOACK="! -y" ; fi
				POLICY=`echo ${POLICY} | tr a-z A-Z`
				case ${DIRLETTER} in
				I)		DIR="input${NOLOGSUFFIX}"	;;
				O)		DIR="output${NOLOGSUFFIX}"	;;
				F)		DIR="forward${NOLOGSUFFIX}"	;;
				esac
				#echo X${DIR}X
				eval "/sbin/ipchains -I ${DIR} 1 -i ${IF} -p ${PROTO} ${DOACK} -s ${SRCHOST} ${SRCPORT} -d ${DESTHOST} ${DESTPORT} -j ${POLICY}"
			fi # no need to handle DOCOMMAND=none :-)
		    LAST5=${LAST4}
		    LAST4=${LAST3}
		    LAST3=${LAST2}
		    LAST2=${LAST1}
		    LAST1=${CURRENT}									;;
		esac

#Debug
		if [ "${DEBUG}" = "YES" ]; then 
		    echo src= ${SRCIP} ${SRCPORT} dest= ${DESTIP} ${DESTPORT}
		    echo if= ${IF} proto= ${PROTO}
		    echo
		fi #if debugging
	fi #if ISLOGLINE

#Get the next log entry and start over.
	unset ACK COMMENT DEST DFFLAG DESTHOST DESTIP DESTPORT DIR DOACK ECHOACK \
	FFLAG FOFLAG IF IFLAG ISLOGLINE \
	LINEHASDYNAMIC LFLAG MESSPOL POLICY PROTO SFLAG SRC SRCHOST SRCIP SRCPORT TAIL TFLAG 
    #read J1 J2 J3 J4 J5 J6 DIR MESSPOL IF PROTO SRC DEST \
    #     LFLAG SFLAG IFLAG FOFLAG TFLAG DFFLAG TAIL
	A7=${O7} ; A8=${O8} ; A9=${O9} ; A10=${O10} ; A11=${O11} ; A12=${O12} ; A13=${O13}
	O7=${J7} ; O8=${J8} ; O9=${J9} ; O10=${J10} ; O11=${J11} ; O12=${J12} ; O13=${J13}
	read J1 J2 J3 J4 J5 J6 J7 J8 J9 J10 J11 J12 J13 J14 J15 J16 J17 J18 J19
#Keep reading until a line with different firewall values is found
#FIXME - handle ping-pong where line matches either last or next to last.
	while { [ "${O7}"  = "${J7}"  ] && [ "${O8}"  = "${J8}"  ] && [ "${O9}"  = "${J9}"  ] && [ "${O10}" = "${J10}" ] && \
            [ "${O11}" = "${J11}" ] && [ "${O12}" = "${J12}" ] && [ "${O13}" = "${J13}" ] ; } ||
		  { [ "${A7}"  = "${J7}"  ] && [ "${A8}"  = "${J8}"  ] && [ "${A9}"  = "${J9}"  ] && [ "${A10}" = "${J10}" ] && \
            [ "${A11}" = "${J11}" ] && [ "${A12}" = "${J12}" ] && [ "${A13}" = "${J13}" ] ; } ; do 
		if [ "${HEARTBEAT}" = "yes" ]; then echo -n "-" >/dev/stderr ; NEEDLF="YES" ; fi
		A7=${O7} ; A8=${O8} ; A9=${O9} ; A10=${O10} ; A11=${O11} ; A12=${O12} ; A13=${O13}
		O7=${J7} ; O8=${J8} ; O9=${J9} ; O10=${J10} ; O11=${J11} ; O12=${J12} ; O13=${J13}
		read J1 J2 J3 J4 J5 J6 J7 J8 J9 J10 J11 J12 J13 J14 J15 J16 J17 J18 J19
	done
done

if [ "${NEEDLF}" = "YES" ]; then echo >/dev/stderr ; NEEDLF="NO" ; fi
if [ "${EXITMASON}" = "YES" ]; then
	echo Mason is exiting because of a SIGHUP or EXITMASON=YES. >/dev/stderr
else
	echo Mason is exiting because of an end of input data. >/dev/stderr
fi


	#unset J1 J2 J3 J4 J5 J6 J7 J8 J9 J10 J11 J12 J13 J14 J15 J16 J17 J18 J19 \
#	                  O7 O8 O9 O10 O11 O12 O13 \
#	                  A7 A8 A9 A10 A11 A12 A13 \
