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


#-------------------------------------------------------------------------
# User customizable options below
#-------------------------------------------------------------------------

#NAMECACHE _could_ be /etc/hosts, but this was really intended to be a
#local cache for Mason only.  This really should be in some directory like
#/var/lib/mason.
NAMECACHE="/tmp/morehosts"
NETCACHE="/tmp/netconvert"
NOLOGSUFFIX="N"	#If you change this, change it in ruleshell too.

#If you change this, make sure an identical value is placed in 
#mason_policy as well.
POLICYFILE="/tmp/current_policy"

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

# "YES" to debug, anything else = dont
DEBUG="NO"

# "ipchains" = echo ipchains command to STDOUT, "ipfwadm" = echo 
# ipfwadm command to STDOUT, "none" = don't echo either.
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


# "ipchains" = actually run the ipchains command, "ipfwadm" = actually 
# run the ipfwadm command, "no" = don't run either.  no is useful if you're 
# not running Mason as root or are running Mason on some machine other
# than the actual operating firewall.  User can override either by simply 
# setting the environment variable ahead of time.
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

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 ${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




# "yes" = echo dot to STDERR when processing a repeat line, 
# "no" = don't.
case ${HEARTBEAT} in
yes|YES)			HEARTBEAT="yes"	;;
no|NO|none|NONE)	HEARTBEAT="no"	;;
*)					HEARTBEAT="yes"	;;
esac


# What should the IP address be converted to?
# network: the smallest network in the routing table that contains the address.
# no: leave IP address as is.
# custom: to be implemented.
# dynamic IP's are replaced with ${ifNADDR} solely based on the value of DYNIF
case ${IPCONV} in
hostname|HOSTNAME)	IPCONV="hostname"	;;
network|NETWORK)	IPCONV="network"	;;
no|NO|none|NONE)	IPCONV="none"		;;
#custom|CUSTOM)		IPCONV="custom"		;;
*)					IPCONV="network"	;;
esac



#-------------------------------------------------------------------------
# Probably not a good idea to edit below this line...
#-------------------------------------------------------------------------

#Beeps to be added later
#DOBEEP="YES"		# "YES" = beep at user with new rule, "NO" = dont

LAST1="" ; LAST2="" ; LAST3="" ; LAST4="" ; LAST5="" ; CURRENT=""

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

#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"
		    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
	        	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 ?
	        	*)			SRCPORT="1024:65535"		;;
	        	esac
	    	fi

		    MATCHSERVICE=`cat /etc/services | grep "${DESTPORT}/${PROTO}" | head -n 1`
		    if [ -n "${MATCHSERVICE}" ]; then
		        DESTPORT=`echo ${MATCHSERVICE} | awk '{print $1}'`
		    else
		        case ${DESTPORT} in
		        [0-9]|[1-9][0-9]|[1-9][0-9][0-9]|10[0-1][0-9]|102[0-3]) : ;;
		        *)			DESTPORT="1024:65535"		;;
		        esac
		    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 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
		    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}"				;;
			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




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