%{
/*
     extract - A network log processor
     Copyright (C) 1993 Douglas Lee Schales, David K. Hess, David R. Safford

     Please see the file `COPYING' for the complete copyright notice.

grammar.y - 03/20/93

*/
#include <stdio.h>
#include <memory.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <malloc.h>
#include "lex.h"
#include "chario.h"
#include "parser.h"
#include "builder.h"
#include "timesub.h"
#include "stdunix.h"

extern int parse_error;
#define TCPDATA 0
#define UDPDATA 1
#define ICMPDATA 2
extern int protoflag;

struct icmptypes {
     char *name;
     unsigned short type;
};

static struct icmptypes icmptbl[] = {
{ "echorep", 0x0000 },
{ "netunreach", 0x0300 },
{ "hostunreach", 0x0301 },
{ "protounreach", 0x0302 },
{ "portunreach", 0x0303 },
{ "fraglost", 0x0304 },
{ "badsrcroute", 0x0305 },
{ "srcquench", 0x0400 },
{ "netredir", 0x0500 },
{ "hostredir", 0x0501 },
{ "tosnetredir", 0x0502 },
{ "toshostredir", 0x0503 },
{ "echoreq", 0x0800 },
{ "tstampreq", 0x0d00 },
{ "tstamprep", 0x0e00 },
{ "inforeq", 0x0f00 },
{ "inforep", 0x1000 },
{ "maskreq", 0x1100 },
{ "maskrep", 0x1200 },
{ 0, 0 }
};

%}

%union {
     unsigned int intval;
     unsigned long *intpval;
     char *strval;
}

%token DSTPORT SRCPORT DSTADDR SRCADDR SRCNET DSTNET DATE TIME HOST NET
%token TODAY YEST INCLUDE
%token OR AND MASKOPER NOT SINCE BEFORE
%token NEQ LEQ GEQ
%token INTEGER BYTEVAL STRING
%token PRINT NEXT
%type <intval> INTEGER BYTEVAL
%type <intval> OR AND
%type <intval> oper NEQ LEQ GEQ '=' '<' '>' '!' NOT 
%type <intval> netop
%type <intval> DATE TIME TODAY YEST SINCE BEFORE
%type <intval> SRCADDR DSTADDR SRCPORT DSTPORT SRCNET DSTNET HOST NET
%type <intval> INCLUDE
%type <intval> PRINT NEXT
%type <strval> STRING
%type <intval> ip_address netmask integer tcpport datespec timespec
%type <intpval> hostspec
%left OR
%left AND
%right '!'
%%
file
     : stmt file
     | stmt 
;

stmt
     : cond '{' actionset '}' {
	  queuestmt();
     }
     | INCLUDE STRING ';' {
	  if(includefile($2) != 0){
	       fprintf(stderr, "\"%s\", line %d: include file error.\n",
		       getfilename(), getlinenum());
	       parse_error = 1;
	  }
     }
;

cond
     : /* */
     | cond OR cond {
          pushoper($2);
     }
     | cond AND cond {
	  pushoper($2);
     }
     | term
;

actionset
     : action ';' actionset
     | action ';'
     | action
;

action
     : PRINT {
	  queueact($1);
     }
     | NEXT {
          queueact($1);
     }
     | error
;

term
     : SRCPORT oper tcpport {
	  pushkey(SRCPORT);
	  pushval($3);
	  pushoper($2);
     }
     | DSTPORT oper tcpport {
	  pushkey(DSTPORT);
	  pushval($3);
	  pushoper($2);
     }
     | HOST oper hostspec {
	  unsigned long *addrp;
	  int i;
	  addrp = $3;
	  if(addrp){
	       i = 0;
	       do {
		    pushkey(SRCADDR);
		    pushval(addrp[i]);
		    pushoper($2);
		    pushkey(DSTADDR);
		    pushval(addrp[i]);
		    pushoper($2);
		    pushoper(OR);
		    if(i)
			 pushoper(OR);
	       } while(addrp[++i]);
	       free(addrp);
	  }
	  else switch($2){
	  case '=':
	       pushval(0);
	       break;
	  default:
	       pushval(1);
	       break;
	  }
     }
     | SRCADDR oper hostspec {
	  unsigned long *addrp;
	  int i;

	  addrp = $3;
	  if(addrp){
	       i = 0;
	       do {
		    pushkey(SRCADDR);
		    pushval(addrp[i]);
		    pushoper($2);
		    if(i)
			 pushoper(OR);
	       } while(addrp[++i]);
	       free(addrp);
	  }
	  else switch($2){
	  case '=':
	       pushval(0);
	       break;
	  default:
	       pushval(1);
	       break;
	  }
     }
     | DSTADDR oper hostspec {
	  unsigned long *addrp;
	  int i;

	  addrp = $3;
	  if(addrp){
	       i = 0;
	       do {
		    pushkey(DSTADDR);
		    pushval(addrp[i]);
		    pushoper($2);
		    if(i)
			 pushoper(OR);
	       } while(addrp[++i]);
	       free(addrp);
	  }
	  else switch($2){
	  case '=':
	       pushval(0);
	       break;
	  default:
	       pushval(1);
	       break;
	  }
     }
     | NET netop hostspec {
	  unsigned long *addrp;
	  int i;

	  addrp = $3;
	  if(addrp){
	       i = 0;
	       do {
		    unsigned long mask = getmask(addrp[i]);
		    pushval(addrp[i]);
		    pushval(mask);
		    pushoper(MASKOPER); /* net & mask */
		    pushkey(SRCNET);
		    pushval(mask);
		    pushoper(MASKOPER); /* SRCADDR & mask */
		    pushoper($2);
		    pushval(addrp[i]);
		    pushval(mask);
		    pushoper(MASKOPER); /* net & mask */
		    pushkey(DSTNET);
		    pushval(mask);      /* dstaddr & mask */
		    pushoper(MASKOPER);
		    pushoper($2);
		    pushoper(OR);
		    if(i)
			 pushoper(OR);
	       } while(addrp[++i]);
	       free(addrp);
	  }
	  else switch($2){
	  case '=':
	       pushval(0);
	       break;
	  default:
	       pushval(1);
	       break;
	  }
     }
     | NET netop hostspec '/' netmask {
	  unsigned long *addrp;
	  int i;

	  addrp = $3;

	  if(addrp){
	       i = 0;
	       do {
		    pushval(addrp[i]);
		    pushval($5);
		    pushoper(MASKOPER); /* net & mask */
		    pushkey(SRCNET);
		    pushval($5);
		    pushoper(MASKOPER); /* srcaddr & mask */
		    pushoper($2);
		    pushval(addrp[i]);
		    pushval($5);
		    pushoper(MASKOPER); /* net & mask */
		    pushkey(DSTNET);
		    pushval($5);
		    pushoper(MASKOPER); /* dstaddr & mask */
		    pushoper($2);
		    pushoper(OR);
		    if(i)
			 pushoper(OR);
	       } while(addrp[++i]);
	       free(addrp);
	  }
	  else switch($2){
	  case '=':
	       pushval(0);
	       break;
	  default:
	       pushval(1);
	       break;
	  }
     }
     | SRCNET netop hostspec {
	  unsigned long *addrp;
	  int i;

	  addrp=$3;
	  if(addrp){
	       i = 0;
	       do {
		    unsigned long mask = getmask(addrp[i]);
		    pushval(addrp[i]);
		    pushval(mask);
		    pushoper(MASKOPER);
		    pushkey(SRCNET);
		    pushval(mask);
		    pushoper(MASKOPER);
		    pushoper($2);
		    if(i)
			 pushoper(OR);
	       } while(addrp[++i]);
	       free(addrp);
	  }
	  else switch($2){
	  case '=':
	       pushval(0);
	       break;
	  default:
	       pushval(1);
	       break;
	  }
     }
     | DSTNET netop hostspec {
	  unsigned long *addrp;
	  int i;

	  addrp = $3;
	  if(addrp){
	       i = 0;
	       do {
		    unsigned long mask = getmask(addrp[i]);
		    pushval(addrp[i]);
		    pushval(mask);
		    pushoper(MASKOPER);
		    pushkey(DSTNET);
		    pushval(mask);
		    pushoper(MASKOPER);
		    pushoper($2);
		    if(i)
			 pushoper(OR);
	       } while(addrp[++i]);
	       free(addrp);
	  }
	  else switch($2){
	  case '=':
	       pushval(0);
	       break;
	  default:
	       pushval(1);
	       break;
	  }
     }
     | SRCNET netop hostspec '/' netmask {
	  unsigned long *addrp;
	  int i;

	  addrp = $3;
	  if(addrp){
	       i = 0;
	       do {
		    pushval(addrp[i]);
		    pushval($5);
		    pushoper(MASKOPER);
		    pushkey(SRCNET);
		    pushval($5);
		    pushoper(MASKOPER);
		    pushoper($2);
		    if(i)
			 pushoper(OR);
	       } while(addrp[++i]);
	       free(addrp);
	  }
	  else switch($2){
	  case '=':
	       pushval(0);
	       break;
	  default:
	       pushval(1);
	       break;
	  }
     }
     | DSTNET netop hostspec '/' netmask {
	  unsigned long *addrp;
	  int i;

	  addrp=$3;
	  if(addrp){
	       i = 0;
	       do {
		    pushval(addrp[i]);
		    pushval($5);
		    pushoper(MASKOPER);
		    pushkey(DSTNET);
		    pushval($5);
		    pushoper(MASKOPER);
		    pushoper($2);
		    if(i)
			 pushoper(i);
	       } while(addrp[++i]);
	       free(addrp);
	  }
	  else switch($2){
	  case '=':
	       pushval(0);
	       break;
	  default:
	       pushval(1);
	       break;
	  }
     }
     | DATE oper datespec {
          switch($2){
	  case '>':
	  case LEQ:
	       pushkey($1);
	       pushval(timeofday($3, 23, 59, 59));
	       pushoper($2);
	       break;
	  case '=':
	       pushkey($1);
	       pushval($3);
	       pushoper(GEQ);
	       pushkey($1);
	       pushval(timeofday($3, 23, 59, 59));
	       pushoper(LEQ);
	       pushoper(AND);
	       break;
	  case NEQ:
	       pushkey($1);
	       pushval($3);
	       pushoper('<');
	       pushkey($1);
	       pushval(timeofday($3, 23, 59, 59));
	       pushoper('>');
	       pushoper(OR);
	       break;
	  default:
	       pushkey($1);
	       pushval($3);
	       pushoper($2);
	       break;
	  }
     }
     | TIME oper timespec {
	  pushkey($1);
	  pushval($3);
	  pushoper($2);
     }
     | SINCE datespec BYTEVAL ':' BYTEVAL {
          pushkey(DATE);
          pushval(timeofday($2, $3, $5, 0));
          pushoper(GEQ);
     }
     | SINCE datespec BYTEVAL ':' BYTEVAL ':' BYTEVAL {
          pushkey(DATE);
          pushval(timeofday($2, $3, $5, $7));
          pushoper(GEQ);
     }
     | SINCE datespec {
          pushkey(DATE);
          pushval($2);
          pushoper(GEQ);
     }
     | BEFORE datespec BYTEVAL ':' BYTEVAL {
          pushkey(DATE);
          pushval(timeofday($2, $3, $5, 0));
          pushoper('<');
     }
     | BEFORE datespec BYTEVAL ':' BYTEVAL ':' BYTEVAL {
          pushkey(DATE);
          pushval(timeofday($2, $3, $5, $7));
          pushoper('<');
     }
     | BEFORE datespec {
          pushkey(DATE);
          pushval($2);
          pushoper('<');
     }
     | '(' cond ')'
     | '!' term { pushuoper($1); }
     | NOT term { pushuoper('!'); }
     | error { pushval(0); }
;

oper
     : '=' {$$ = $1;}
     | '>' {$$ = $1;}
     | '<' {$$ = $1;}
     | NEQ {$$ = $1;}
     | LEQ {$$ = $1;}
     | GEQ {$$ = $1;}
     | error { pushoper(0); }
;

netop
     : '=' {$$ = $1;}
     | NEQ {$$ = $1;}
     | error { pushoper(0); }
;

datespec
     : BYTEVAL '/' BYTEVAL {
	  int year = getyear();
	  if(year < 100)
	       year += 1900;
	  if($1 < 1 || $1 > 12 ||
	     !validmday($1, $3, year)){
	       fprintf(stderr, "\"%s\", line %d: malformed date.\n",
		       getfilename(), getlinenum());
	       parse_error = 1;
	  }
	  else
	       $$ = makedate($1, $3, year);
     }
     | BYTEVAL '/' BYTEVAL '/' integer {
	  int year = $5;
	  if(year < 100)
	       year += 1900;
	  if($1 < 1 || $1 > 12 ||
	     !validmday($1, $3, year)){
	       fprintf(stderr, "\"%s\", line %d: malformed date.\n",
		       getfilename(), getlinenum());
	       parse_error = 1;
	  }
	  else
	       $$ = makedate($1, $3, year);
     }
     | TODAY {
          $$ = today();
     }
     | YEST {
          $$ = yesterday();
     }
     | error { $$ = 0; }
;	       

timespec
     : BYTEVAL ':' BYTEVAL {
	  $$ = $1*3600 + $3*60;
     }
     | BYTEVAL ':' BYTEVAL ':' BYTEVAL {
	  $$ = $1*3600 + $3*60 + $5;
     }
     | error { $$ = 0; }
;


tcpport
     : integer { $$ = $1; }
     | STRING {
	  if(protoflag != ICMPDATA){
             struct servent *se;
             if((se = getservbyname($1,
				    protoflag == TCPDATA ? "tcp" : "udp"))){
		  $$ = htonl(se->s_port);
	     }
	     else {
		  fprintf(stderr, "\"%s\", line %d: unknown service: %s\n", 
			  getfilename(), getlinenum(),$1);
		  parse_error = 1;
		  $$ = 0;
	     }
          }
          else {
             int i;
             for(i=0;icmptbl[i].name;i++)
                  if(strcasecmp(icmptbl[i].name, $1) == 0)
                      break;       
             if(icmptbl[i].name)
                  $$ = icmptbl[i].type;
             else {
                  fprintf(stderr, "\"%s\", line %d: unknown ICMP type: %s\n",
                          getfilename(), getlinenum(), $1);
                  parse_error = 1;
                  $$ = 0xffff;
             }
          }
          free($1);
     }
     | error {$$ = 0;}
;

integer
     : INTEGER {
          $$ = $1;
     }
     | BYTEVAL {
          $$ = $1;
     }
;

hostspec
     : ip_address { 
	  unsigned long *addrp;
	  addrp = (unsigned long *)malloc(sizeof(unsigned long) * 2);
	  addrp[0] = $1;
	  addrp[1] = 0;
	  $$ = addrp;
     }
     | STRING {
          struct hostent *he;
	  struct {
	       int ipaddr:32;
	  } host;
	  if((he = gethostbyname($1))){
	       int num;
	       unsigned long *addrp;
	       for(num=0;he->h_addr_list[num];num++)
		    ;
	       addrp = (unsigned long *)malloc((num+1)*sizeof(unsigned long));
	       addrp[num] = 0;
	       for(num--;num>=0;num--)
		    memcpy(&addrp[num], he->h_addr_list[num], 4);
	       $$ = addrp;
	  }
	  else {
	       fprintf(stderr, "\"%s\", line %d: unknown host: %s\n", 
		       getfilename(), getlinenum(), $1);
	       $$ = 0;
	  }
	  free($1);
     }
     | error { $$ = 0; }
;

netmask
     : ip_address { $$=$1;}
     
;

ip_address
	: BYTEVAL '.' BYTEVAL '.' BYTEVAL '.' BYTEVAL {
	     $$ = (($1 << 24 | $3 << 16 | $5 << 8 | $7));
	}
;

%%

     struct keywords keywords[] = {
     { "dstport", DSTPORT },
     { "srcport", SRCPORT },
     { "srchost", SRCADDR },
     { "dsthost", DSTADDR },
     { "srcnet", SRCNET },
     { "dstnet", DSTNET },
     { "host", HOST },
     { "net", NET },
     { "time", TIME },
     { "date", DATE },
     { "since", SINCE },
     { "before", BEFORE },
     { "today", TODAY },
     { "yesterday", YEST },
     { "include", INCLUDE },
     { "or", OR },
     { "and", AND },
     { "not", NOT }, 
     { "print", PRINT },
     { "next", NEXT },
     { (char *)0, 0 }
     };

void
yyerror()
{
     fprintf(stderr, "\"%s\", line %d: syntax error.\n",
	     getfilename(), getlinenum());
     parse_error = 1;
}

unsigned long
getmask(unsigned long addr)
{
     if(IN_CLASSA(addr))
	  return IN_CLASSA_NET;
     else if(IN_CLASSB(addr))
	  return IN_CLASSB_NET;
     else if(IN_CLASSC(addr))
	  return IN_CLASSC_NET;
     else
	  return 0xffffffff;
}

