#!/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.
#-------------------------------------------------------------------------


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.
				if ! /sbin/ipchains -L -n ${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
}



nameof () {
#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)
			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
	#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

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						;;
        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						;;
        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					;;
        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.

		nameof ${SRCIP}
		SRCHOST=${RETVAL}

		nameof ${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"
#generalize ssh client (1023 and down) to 1000:1023 ?
			if [ "${SRCPORT}/${PROTO}" = "22/tcp" ]; then
					if [ ${DESTPORT} -ge $PORT_MASQ_BEGIN ] && [ ${DESTPORT} -le ${PORT_MASQ_END} ]; then
						DESTPORT="${PORT_MASQ_BEGIN}:${PORT_MASQ_END}"
					else
				        case ${DESTPORT} in
				        [0-9]|[1-9][0-9]|[1-9][0-9][0-9]|10[0-1][0-9]|102[0-3])
							while [ "${DESTPORT}" -lt "${LOWSSHPORT}" ]; do
								LOWSSHPORT=`expr ${LOWSSHPORT} - 10`
							done
							DESTPORT="${LOWSSHPORT}:1023"
																;;
				        *)	COMMENT2="${COMMENT2} D=${DESTPORT}" ; DESTPORT="1024:65535"		;;
				        esac
					fi
			elif [ "${DESTPORT}/${PROTO}" = "22/tcp" ]; then
					if [ ${SRCPORT} -ge $PORT_MASQ_BEGIN ] && [ ${SRCPORT} -le ${PORT_MASQ_END} ]; then
						SRCPORT="${PORT_MASQ_BEGIN}:${PORT_MASQ_END}"
					else
				    	case ${SRCPORT} in
		        		[0-9]|[1-9][0-9]|[1-9][0-9][0-9]|10[0-1][0-9]|102[0-3])
							while [ "${SRCPORT}" -lt "${LOWSSHPORT}" ]; do
								LOWSSHPORT=`expr ${LOWSSHPORT} - 10`
							done
							SRCPORT="${LOWSSHPORT}:1023"
																;;
	        			*)	COMMENT2="${COMMENT2} S=${SRCPORT}" ; SRCPORT="1024:65535"		;;
	        			esac
					fi
			else
			    MATCHSERVICE=`cat /etc/services | grep "${SRCPORT}/${PROTO}" | head -n 1`
			    if [ -n "${MATCHSERVICE}" ]; then
					if [ "${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
			        SRCPORT=`echo ${MATCHSERVICE} | awk '{print $1}'`
		    	else
					if [ ${SRCPORT} -ge $PORT_MASQ_BEGIN ] && [ ${SRCPORT} -le ${PORT_MASQ_END} ]; then
						SRCPORT="${PORT_MASQ_BEGIN}:${PORT_MASQ_END}"
					else
		    	    	case ${SRCPORT} in
		        		[0-9]|[1-9][0-9]|[1-9][0-9][0-9]|10[0-1][0-9]|102[0-3])	: ;;
#FIXME generalize ssh client (1023 and down) to 1000:1023 ?
	    	    		*)	COMMENT2="${COMMENT2} S=${SRCPORT}" ; SRCPORT="1024:65535"		;;
	        			esac
					fi
	    		fi

			    MATCHSERVICE=`cat /etc/services | grep "${DESTPORT}/${PROTO}" | head -n 1`
			    if [ -n "${MATCHSERVICE}" ]; then
			        DESTPORT=`echo ${MATCHSERVICE} | awk '{print $1}'`
			    else
					if [ ${DESTPORT} -ge $PORT_MASQ_BEGIN ] && [ ${DESTPORT} -le ${PORT_MASQ_END} ]; then
						DESTPORT="${PORT_MASQ_BEGIN}:${PORT_MASQ_END}"
					else
				        case ${DESTPORT} in
				        [0-9]|[1-9][0-9]|[1-9][0-9][0-9]|10[0-1][0-9]|102[0-3]) : ;;
				        *)	COMMENT2="${COMMENT2} D=${DESTPORT}" ; DESTPORT="1024:65535"		;;
				        esac
					fi
			    fi
			fi
		fi

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