/*
 *   logtool - a logfile parsing/monitoring/manipulation utility
 *
 *   Copyright (C) Y2K (2000) A.L.Lambert
 *
 *   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, 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.
 */

/* Yee ole includes (I put this all in one file for my sanity) */
#include "includes.h"
       


/* 
 * NOTES:
 *
 * This is some LONG and UGLY code.  I think there's gotta be a way to
 * simplify it a whole lot, and to clean up some of the ugly, but for now
 * it's getting the job done.  Too much stuff in one file for my tastes too. 
 * :(
 *
 * Also, all yee would be code tweakers; the order in which these functions
 * are called is IMPORTANT!!! Each parser function modifies event.message,
 * and all subsequent parsing efforts expect previous ones to have been 
 * completed.  YOU WERE WARNED!!!
 * 
 */

/* function to parse up the SID's from event.message (if exists)	*/
short int ltm_snort_psids(char *sids) {
	/* some tmp data storage	*/
	char tmp_str[LSIZE];
	long int i;

	/* if we got SID information at the beginning of the message, then crunch it into separate variable	*/
	if(lt_match(event.message, "^\\[.*:.*:.*\\] .*") == TRUE) {
		sscanf(event.message, "%s %[^\n]", sids, tmp_str);	/* strip the first string	*/
		strcpy(event.message, tmp_str);	/* copy the remainder back to event.message		*/
		strcpy(tmp_str, "");
		if(cf.outfmt == OUTPUT_ANSI || cf.outfmt == OUTPUT_HTML) {
			for (i = 0 ; sids[i] != '\0' ; ++i ) {
				if(sids[i] == '[') {
					strcpy(tmp_str, "\033l[\033e\033c");
				} else if(sids[i] == ']') {
					strcat(tmp_str, "\033e\033l]\033e");
				} else {
					sprintf(tmp_str, "%s%c", tmp_str, sids[i]);
				}
			}
			strcpy(sids, tmp_str);
		}
	}
	/* add a space to the end of it so it doesn't look so cramped later	*/
	return 0;
}

/* function to parse the (snort_preprocessor) text field (if exist)	*/
short int ltm_snort_pproc(char *pproc) {
	/* some tmp data storage	*/
	char tmp_str[LSIZE];
	long int i;
	/* see notes in ltm_snort_psids(); above		*/
	if(lt_match(event.message, "^\\(.*_.*\\) ") == TRUE) {
		sscanf(event.message, "%s %[^\n]", pproc, tmp_str);
		strcpy(event.message, tmp_str);
		
		/* do some special effects if our output format == ansi	*/
		if(cf.outfmt == OUTPUT_ANSI || cf.outfmt == OUTPUT_HTML) {
			for (i = 0 ; pproc[i] != '\0' ; ++i ) {
				if(pproc[i] == '(') {
					strcpy(tmp_str, "\033l(\033e\033c");
				} else if(pproc[i] == ')') {
					strcat(tmp_str, "\033e\033l)\033e");
				} else {
					sprintf(tmp_str, "%s%c", tmp_str, pproc[i]);
				}
			}
			strcpy(pproc, tmp_str);
		}
	}
	return 0;
}

/* function to parse the "x.x.x.x:x -> x.x.x.x:x" from the end of a Snort message (if exist)	*/
short int ltm_snort_pips(char *src, char *src_prt, char *dst, char *dst_prt, short int resolv) {
	long int i, j;
	char *ptr;
	char tmp_str[LSIZE];
	ptr = event.message;

	/* backspace from the end of the line, counting spaces to three	*/	
	for(i = (strlen(event.message) - 1), j = 0; j < 3; --i) {
		if(event.message[i] == ' ') ++j;
	}
	/* increment the ptr to the appropriate spot	*/
	ptr = (ptr + i + 1);

	/* null byte it (will insert null byte into event.message 
	 * to clip the part we're about to parse separately) */
	ptr[0] = '\0';
	++ptr;	/* move ptr forward so we can work with data directly	*/

	if(strstr(ptr, ":") != NULL) {	/* then we've got a src:src_prt scenario	*/
		sscanf(ptr, "%[^:]:%s -> %[^:]:%s", src, src_prt, dst, dst_prt);
	} else {	/* this was probably an ICMP message; IP's only			*/
		sscanf(ptr, "%s -> %s", src, dst);
		strcpy(dst_prt, "");	/* overkill way to make sure variable is blank	*/
		strcpy(src_prt, "");
	}
	if(resolv == TRUE) {	
		ptr = get_host(src);
		if(ptr != NULL) { 
			strcpy(tmp_str, src);
			strcpy(src, ptr);
			strcat(src, "(");
			strcat(src, tmp_str);
			strcat(src, ")");
		}
		ptr = get_host(dst);
		if(ptr != NULL) {
			strcpy(tmp_str, dst);
			strcpy(dst, ptr);
			strcat(dst, "(");
			strcat(dst, tmp_str);
			strcat(dst, ")");
		}
		if(cf.outfmt == OUTPUT_ANSI || cf.outfmt == OUTPUT_HTML) {
	                sprintf(tmp_str, "\033l(\033e\033w");
	                lt_strep(src, 512, "(", tmp_str);
	                lt_strep(dst, 512, "(", tmp_str);
	                
			sprintf(tmp_str, "\033l)\033e");
	                lt_strep(src, 512, ")", tmp_str);
	                lt_strep(dst, 512, ")", tmp_str);
		}
	}
	
	/* if we're making an ANSI event, then do some special fancy tricks.		*/
	if(cf.outfmt == OUTPUT_ANSI || cf.outfmt == OUTPUT_HTML) { 
		/* yeah, I could do all the below as a single sprintf(), and I may be doing
		 * that soon. */
		
		strcpy(tmp_str, "\033W");
		strcat(tmp_str, src);
		strcat(tmp_str, "\033e");
		strcpy(src, tmp_str);
		/* ditto	*/
		strcpy(tmp_str, "\033W");
		strcat(tmp_str, dst);
		strcat(tmp_str, "\033e");
		strcpy(dst, tmp_str);
		/* if we got src/dst port's, then do the deed to them too	*/
		if(src_prt[0] != '\0') {
			strcpy(tmp_str, "\033W");
			strcat(tmp_str, src_prt);
			strcat(tmp_str, "\033e");
			strcpy(src_prt, tmp_str);
		
			strcpy(tmp_str, "\033W");
			strcat(tmp_str, dst_prt);
			strcat(tmp_str, "\033e");
			strcpy(dst_prt, tmp_str);
		}
	}
	
	return 0;	
}

/* function to parse the {PROTOCOL} text from a Snort event (if exist)			*/
short int ltm_snort_pproto(char *proto) {

	char *ptr = NULL;
	char tmp_str[LSIZE];
	
	/* if we got a TCP, UDP, or ICMP message		*/
	if(lt_match(event.message, "\\{TCP\\}$|\\{UDP\\}$|\\{ICMP\\}$") == TRUE) {
		/* parse it out of there 	*/
		ptr = strrchr(event.message, ' ');
		ptr[0] = '\0';
		++ptr;
		strcpy(proto, ptr);
	}

	/* you know about the ANSI prettyfication by now, don't you?	*/
	if(cf.outfmt == OUTPUT_ANSI || cf.outfmt == OUTPUT_HTML) {
		strcpy(tmp_str, "\033l");
		strcat(tmp_str, "{");
		strcat(tmp_str, "\033e");
		strcat(tmp_str, "\033C");
		lt_strep(proto, 512, "{", tmp_str);
		strcpy(tmp_str, "\033l");
		strcat(tmp_str, "}");
		strcat(tmp_str, "\033e");
		lt_strep(proto, 512, "}", tmp_str);
	}
	return 0;
}


/* function to parse the [Priority: .*] text from a Snort event (if exist)		*/
short int ltm_snort_pprior(char *prior) {
	char *ptr;
	char tmp_str[LSIZE];

	if((ptr = strstr(event.message, "[Priority: ")) != NULL) {
		strcpy(prior, ptr);
		--ptr;
		ptr[0] = '\0';
	}
	lt_strep(prior, 512, "[Priority: ", "[Pri: ");	
	if(cf.outfmt == OUTPUT_ANSI || cf.outfmt == OUTPUT_HTML) {
		strcpy(tmp_str, "\033l");
		strcat(tmp_str, "[");
		strcat(tmp_str, "\033e");
		strcat(tmp_str, "\033c");
		lt_strep(prior, 512, "[", tmp_str);
		strcpy(tmp_str, "\033l");
		strcat(tmp_str, "]");
		strcat(tmp_str, "\033e");
		lt_strep(prior, 512, "]", tmp_str);
	}

	return 0;
}

/* function to parse the [Classification: .*] text from a snrot event (if exist)	*/
short int ltm_snort_pclass(char *class) {
	char *ptr;
	char tmp_str[LSIZE];

	if((ptr = strstr(event.message, "[Classification: ")) != NULL) {
		strcpy(class, ptr);
		--ptr;
		ptr[0] = '\0';
	}
	lt_strep(class, 512, "[Classification: ", "[");

	if(cf.outfmt == OUTPUT_ANSI || cf.outfmt == OUTPUT_HTML) {
		strcpy(tmp_str, "\033l");
		strcat(tmp_str, "[");
		strcat(tmp_str, "\033e");
		strcat(tmp_str, "\033c");
		lt_strep(class, 512, "[", tmp_str);
		strcpy(tmp_str, "\033l");
		strcat(tmp_str, "]");
		strcat(tmp_str, "\033e");
		lt_strep(class, 512, "]", tmp_str);
	}
	return 0;

}

/* 
 * yee ole showtime function.   This is the only function which should be 
 * called by any external routines.
 */


short int ltm_snort() {

	/* various varibles and such are in mods.h struct SNORT	*/

	/* we use these in parsing and such			*/	
	char msg_tmp[LSIZE];

	/* some TRUE/FALSE variables for display opts		*/
	short int modsn_dispclass = FALSE;
	short int modsn_dispprior = FALSE;
	short int modsn_dispproto = FALSE;
	short int modsn_2lines = FALSE;
	short int modsn_dispsids = FALSE;
	short int modsn_dispproc = FALSE;
	short int modsn_resolvips = FALSE;

	/* setup the display opt's based no environment		*/
	modsn_dispclass = mod_varcheck("modsn_dispclass");
	modsn_dispprior = mod_varcheck("modsn_dispprior");
	modsn_dispproto = mod_varcheck("modsn_dispproto");
	modsn_dispsids = mod_varcheck("modsn_dispsids");
	modsn_dispproc = mod_varcheck("modsn_dispproc");
	modsn_2lines = mod_varcheck("modsn_2lines");
	modsn_resolvips = mod_varcheck("modsn_resolvips");

      	/* Figure out what color this message should be 	*/
        lt_set_event_color();

	/* parse up the basics (date format's, prog/src, etc)	*/
	mod_premsg_setup();

	/* initialize sids, and parse it if we can		*/
	strcpy(sn.sids, "");
	ltm_snort_psids(sn.sids);

	/* ditto for pproc					*/
	strcpy(sn.pproc, "");
	ltm_snort_pproc(sn.pproc);
	
	/* ok, got the first parts of the line processed, head 
	 * for the rear of the line and do the IP's		*/
	ltm_snort_pips(sn.src, sn.src_prt, sn.dst, sn.dst_prt, modsn_resolvips);	
	
	/* see comments above regarding sids and pproc		*/
	strcpy(sn.proto, "");
	ltm_snort_pproto(sn.proto);

	strcpy(sn.prior, "");
	ltm_snort_pprior(sn.prior);
	
	strcpy(sn.class, "");
	ltm_snort_pclass(sn.class);

	/* ok, now we're ready to setup the event.pmsg 
	 * (which is what we show the user later :)
	 *
	 * If we got display orders, and variables not == nothing, then do	*/
	
	/* this puts the main message, in appropriate colors, into the output	*/	
	sprintf(msg_tmp, "%s%s%s", event.pcolor, event.message, "\033e");
	strcpy(sn.msg, event.message);
	strcat(event.pmsg, " ");
	strcat(event.pmsg, msg_tmp);
	
	if(modsn_dispproc == TRUE && sn.pproc[0] != '\0') {
		strcat(event.pmsg, " ");
		strcat(event.pmsg, sn.pproc);
	}
	
	if(modsn_dispsids == TRUE && sn.sids[0] != '\0') { 
		strcat(event.pmsg, " ");
		strcat(event.pmsg, sn.sids);
	}

	
	/* if we want a 2line display, then do this		*/
	if(modsn_2lines == TRUE && cf.outfmt != OUTPUT_CSV) strcat(event.pmsg, "\n\t");

	if(modsn_dispclass == TRUE && sn.class[0] != '\0') {
		strcat(event.pmsg, " ");
		strcat(event.pmsg, sn.class);
	}
	if(modsn_dispprior == TRUE && sn.prior[0] != '\0') {
		strcat(event.pmsg, " ");
		strcat(event.pmsg, sn.prior);
	}
	
	if(modsn_dispproto == TRUE && sn.proto[0] != '\0') {
		strcat(event.pmsg, " ");
		strcat(event.pmsg, sn.proto);
	}

	
	if(sn.src_prt[0] != '\0') {
		if(cf.outfmt == OUTPUT_ANSI || cf.outfmt == OUTPUT_HTML) {
			sprintf(msg_tmp, "%s\033l:%s \033l-> %s\033l:\033e%s", sn.src, sn.src_prt, sn.dst, sn.dst_prt);
		} else {
			sprintf(msg_tmp, "%s:%s -> %s:%s", sn.src, sn.src_prt, sn.dst, sn.dst_prt);
		}
			
	} else {
		if(cf.outfmt == OUTPUT_ANSI || cf.outfmt == OUTPUT_HTML) {
			sprintf(msg_tmp, "%s \033l-> %s", sn.src, sn.dst);
		} else {
			sprintf(msg_tmp, "%s -> %s", sn.src, sn.dst);
		}
	}

	/* final finishing off of event.pmsg			*/
	strcat(event.pmsg, " ");
	strcat(event.pmsg, msg_tmp);
	event.m.snort = &sn;
	/* we did some debug testing with the code block below */ /*
	fprintf(stderr, "msg:%s\n", event.m.snort->msg);
	fprintf(stderr, "dst:prt:%s:%s\n", event.m.snort->dst, event.m.snort->dst_prt);
	fprintf(stderr, "src:prt:%s:%s\n", event.m.snort->src, event.m.snort->src_prt);
	fprintf(stderr, "sids:%s\n", event.m.snort->sids);
	fprintf(stderr, "pproc:%s\n", event.m.snort->pproc);
	fprintf(stderr, "proto:%s\n", event.m.snort->proto);
	fprintf(stderr, "class:%s\n", event.m.snort->class);
	fprintf(stderr, "prior:%s\n", event.m.snort->prior);
	*/
	/* return 0;  We really should be checking for errors above,
	 * and bombing out if we got something bad, but hey, such is life. */
        return 0;

}
