#!/usr/bin/python

# Copyright (C) 2002 John Leach. <john@spammenot.ecsc.co.uk>
# for latest version see http://people.ecsc.co.uk/~john/pp
# based on packetparser.py (c) 2002 John Leach and Greg Sheard

# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

# Change this to "dns = 1" if you want name resolution of IPs
dns = None

import fileinput
import re
from time import ctime, clock,strptime,asctime,time,strftime
from string import atoi,ljust,center,zfill,replace
import socket
import sys

# flag names
tcpflags = {"FIN":1,"SYN":2,"RST":4,"PSH":8,"ACK":16,"URG":32,"ECNE":64,"CWR":128}
ipflags = {"MF":1,"DF":2,"CE":4}

# Validation regexps
typechecks = {	"SRC":		r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}",
		"DST":		r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}",
		"SPT":		r"\d{1,6}",
		"DPT":		r"\d{1,6}",
		"intin":	r"\w{1,12}",
		"intout":	r"\w{1,12}",
		"PROTO":	r"\w{1,6}" }

# ICMP names
icmptype = [	["ECHO_REPLY"],
				[],[],
				["NET_UNREACH","HOST_UNREACH","PROTO_UNREACH","PORT_UNREACH",
				"FRAG_NEEDED_BUT_DF","SOURCE_ROUTE_FAILED","DEST_NET_UNKNOWN",
				"DEST_HOST_UNKNOWN","SOURCE_HOST_ISOLATED","NETWORK_ADMIN_PROHIBITED",
				"HOST_ADMIN_PROHIBITED","NET_TOS_UNREACH","HOST_TOS_UNREACH","TIMESTAMP"],
				["SOURCE_QUENCH"],
				["REDIR_NR","REDIR_HR","REDIR_TOS_NR","REDIR_TOS_HR"],
				[],[],
				["ECHO_REQUEST"],
				[],[],
				["TTL_EXCEEDED_ROUTER","TTL_EXCEEDED_HOST"],
				["PARAM_PROBLEM"],
				["TIMESTAMP_REQUEST"],
				["TIMESTAMP_REPLY"],
				["INFO_REQUEST"],
				["INFO_REPLY"],
				["MASK_REQUEST"],
				["MASQ_REPLY"] ]

# Don't accept lines with these keys null				
notnull = [ "SRC","DST","PROTO" ]
		
# Credit where credits due			
sys.stderr.write("PacketParser v0.2 - John Leach <john@ecsc.co.uk>\n")

# The mother regexp
iptline_regs = re.compile(r"(?:(?P<month>[A-Za-z]{3}) (?: )?(?P<day>\d{1,2}) (?P<time>[\d:]{8}) (?P<uname>[\-\w]+) kernel: )?" \
	"(?:(?P<logmsg>[\w ]+): )?IN=(?P<in>\w*) OUT=(?P<out>\w*) (?:MAC=[\w:]*)?(?P<line>[\s\w\.:=]+)(?:\[[\s\w\.:=]+\])?")

icmpregs = re.compile(r"(.*)(?:[(.*)])?")

linecount = 0
dnscache = {}
try:
	for line in fileinput.input():
		if fileinput.isfirstline():
			sys.stderr.write(("reading from file %s\n" % fileinput.filename()))
		regs = iptline_regs.match(line)
		if regs:
			dict = {"IN":"","OUT":"","DPT":0,"SPT":0,"TYPE":0,"CODE":0,"SRC":"",
				"DST":"","macdst":"","macsrc":"","payload":"","PROTO":"",
				"PROTOID":0,"ipflags":0,"tcpflags":0,"host":""}
				
			if regs.group("month"):
				tme = "%s %s %s" % (regs.group("month"),regs.group("day"),regs.group("time"))
				dict["timestamp"] = strftime("%d-%m %H:%M:%S",strptime(tme,'%b %d %H:%M:%S'))
			else:
				dict["timestamp"] = ""
				
			if (regs.group("logmsg")): dict["logmsg"] = regs.group("logmsg")
			else: dict["logmsg"] = "None"
			
			dict["IN"] = regs.group("in")
			dict["OUT"] = regs.group("out")

			line = regs.group('line')
			
			attrs = re.findall("(?P<key>\w+)=(?P<val>[\w\.\-+]*)", line)
			for pair in attrs:
				dict[pair[0]] = pair[1]
							
			lineflags = re.findall(" (\w{1,3})(?= )",line);
			dict["tcpflags"] = 0
			dict["ipflags"] = 0

			# Figure out what flags are set
			for curflag in lineflags:
				if curflag in tcpflags.keys():
					dict["tcpflags"] = dict["tcpflags"] + tcpflags[curflag]
				else: 
					if curflag in ipflags.keys():
						dict["ipflags"] = dict["ipflags"] + ipflags[curflag]
			# Resolve the protocol name
			try:
				dict["PROTOID"] = socket.getprotobyname(dict["PROTO"])
			except socket.error:
				dict["PROTOID"] = 0
				pass
				
			if (dict["PROTOID"]==1):
				dict["SPT"] = dict["TYPE"]
				dict["DPT"] = dict["CODE"]
			
			# Did somebody say DNS lookups?
			if dns:
				try:
					# Resolve, checking for cached answers first 
					if dict["SRC"] in dnscache.keys():
						dict["shost"] = dnscache[dict["SRC"]]
					else:
						dict["shost"] = socket.gethostbyaddr(dict["SRC"])[0]
						dnscache[dict["SRC"]] = dict["shost"]
					if dict["DST"] in dnscache.keys():
						dict["dhost"] = dnscache[dict["DST"]]
					else:
						dict["dhost"] = socket.gethostbyaddr(dict["DST"])[0]
						dnscache[dict["DST"]] = dict["dhost"]
				except socket.error:
					#sys.stderr.write("gethostbyaddr(%s) failed.\n" % dict["SRC"])
					dict["shost"] = "!"
					dict["dhost"] = "!"
					dnscache[dict["SRC"]] = ""
			else:
			# if not, return nothing
				dict["shost"] = ""
				dict["dhost"] = ""
				
			type_error = None
			for key in dict.keys():
				if (dict[key] != "") and (dict[key] != None) and (key in typechecks.keys()):
					if dict[key]==0: dict[key] = "0"
					if not re.search(typechecks[key],dict[key]):
						sys.stderr.write("%s (%s) failed value check\n" % (key,dict[key]))
						type_error = 1
			
			for fld in notnull:
				if (not dict.has_key(fld)) or (dict[fld] in ["",None]):
					type_error = 1
											
			if not type_error:		
				sys.stdout.write( dict["timestamp"] + " " )
				sys.stdout.write("%s " % dict["logmsg"])
				sys.stdout.write( "[%(IN)s/%(OUT)s] " % dict )
				sys.stdout.write( ljust( dict["PROTO"],5) )
				# Slightly different output if we're resolving
				if dns:
					sys.stdout.write( ljust("%(shost)s=%(SRC)s:%(SPT)s" % dict,22))
					sys.stdout.write( "-> " + ljust("%(dhost)s=%(DST)s:%(DPT)s " % dict,22))
				else:
					sys.stdout.write( ljust("%(SRC)s:%(SPT)s" % dict,22))
					sys.stdout.write( "-> " + ljust("%(DST)s:%(DPT)s " % dict,22))
				sys.stdout.write( "ttl=" + ljust("%(TTL)s" % dict,4 ) )
				sys.stdout.write( "len=" + ljust("%(LEN)s" % dict,6 ) )
				
				# Draw TCP flags
				if dict["PROTOID"]==6:
					sys.stdout.write( "[" )
					for a in tcpflags.keys():
						if (int(dict["tcpflags"]) & tcpflags[a] ) == tcpflags[a]:
							sys.stdout.write(a[0])
					sys.stdout.write( "]" )
				
				# Draw ICMP stuff
				if dict["PROTOID"]==1:
					sys.stdout.write("ICMP_%s" % (icmptype[int(dict["SPT"])][int(dict["DPT"])]) )
				
				sys.stdout.write( "\n" )
		
except IOError:
	sys.stderr.write("IOError: file %s\n" % fileinput.filename())
except KeyboardInterrupt:
	sys.stderr.write("KeyboardInterrupt\n")

