/*
    ettercap -- content filtering engine module

    Copyright (C) ALoR & NaGA

    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.

*/

#include <ec.h>
#include <ec_filter.h>
#include <ec_version.h>
#include <ec_threads.h>
#include <ec_send.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define JIT_FAULT(x, ...) do { USER_MSG("JIT FILTER FAULT: " x "\n", ## __VA_ARGS__); return -E_FATAL; } while(0)

/* since we need a recursive mutex, we cannot initialize it here statically */
static pthread_mutex_t filters_mutex;
#define FILTERS_LOCK     do{ pthread_mutex_lock(&filters_mutex); }while(0)
#define FILTERS_UNLOCK   do{ pthread_mutex_unlock(&filters_mutex); }while(0)

/* protos */

static void reconstruct_strings(struct filter_env *fenv, struct filter_header *fh);
static int compile_regex(struct filter_env *fenv);
   
static int filter_engine(struct filter_op *fop, struct packet_object *po);
static int execute_test(struct filter_op *fop, struct packet_object *po);
static int execute_assign(struct filter_op *fop, struct packet_object *po);
static int execute_incdec(struct filter_op *fop, struct packet_object *po);
static int execute_func(struct filter_op *fop, struct packet_object *po);

static int func_search(struct filter_op *fop, struct packet_object *po);
static int func_regex(struct filter_op *fop, struct packet_object *po);
static int func_pcre(struct filter_op *fop, struct packet_object *po);
static int func_replace(struct filter_op *fop, struct packet_object *po);
static int func_inject(struct filter_op *fop, struct packet_object *po);
static int func_execinject(struct filter_op *fop, struct packet_object *po);
static int func_execreplace(struct filter_op *fop, struct packet_object *po);
static int func_random(struct filter_op *fop, struct packet_object *po);
static int func_log(struct filter_op *fop, struct packet_object *po);
static int func_drop(struct packet_object *po);
static int func_kill(struct packet_object *po);
static int func_exec(struct filter_op *fop);

static int cmp_eq(u_int32 a, u_int32 b);
static int cmp_neq(u_int32 a, u_int32 b);
static int cmp_lt(u_int32 a, u_int32 b);
static int cmp_gt(u_int32 a, u_int32 b);
static int cmp_leq(u_int32 a, u_int32 b);
static int cmp_geq(u_int32 a, u_int32 b);
/*******************************************/

/* initialize the filter mutex */
void filter_init_mutex(void) {
   pthread_mutexattr_t at;
   pthread_mutexattr_init(&at);
   /* we want an recursive mutex, so we can re-acquire it in the same thread */
   pthread_mutexattr_settype(&at, PTHREAD_MUTEX_RECURSIVE_NP);
   pthread_mutex_init(&filters_mutex, &at);
}

/*
 * JIT interpreter for binary filters.
 * it process the filter_ops and apply the instructions
 * on the given packet object
 */
static int filter_engine(struct filter_op *fop, struct packet_object *po)
{
   u_int32 eip = 0;
   u_int32 flags = 0;
      #define FLAG_FALSE   0
      #define FLAG_TRUE    1
  
   /* sanity check */
   BUG_IF(fop == NULL);

   FILTERS_LOCK;

   /* loop until EXIT */
   while (fop[eip].opcode != FOP_EXIT) {

      switch (fop[eip].opcode) {
         case FOP_TEST:
            if (execute_test(&fop[eip], po) == FLAG_TRUE)
               flags |= FLAG_TRUE;
            else
               flags &= ~(FLAG_TRUE);
            
            break;
            
         case FOP_ASSIGN:
            execute_assign(&fop[eip], po);
            /* assignment always returns true */
            flags |= FLAG_TRUE;
            
            break;
            
         case FOP_INC:
         case FOP_DEC:
            execute_incdec(&fop[eip], po);
            /* inc/dec always return true */
            flags |= FLAG_TRUE;
            
            break;
            
         case FOP_FUNC:
            if (execute_func(&fop[eip], po) == FLAG_TRUE)
               flags |= FLAG_TRUE;
            else
               flags &= ~(FLAG_TRUE);

            break;
            
         case FOP_JMP:
            /* jump the the next eip */
            eip = fop[eip].op.jmp;
            continue;

            break;
            
         case FOP_JTRUE:
            /* jump the the next eip if the TRUE FLAG is set*/
            if (flags & FLAG_TRUE) {
               eip = fop[eip].op.jmp;
               continue;
            }
            break;
            
         case FOP_JFALSE:
            /* jump the the next eip if the TRUE FLAG is NOT set */
            if (!(flags & FLAG_TRUE)) {
               eip = fop[eip].op.jmp;
               continue;
            }
            break;
            
         default:
            FILTERS_UNLOCK;
            JIT_FAULT("unsupported opcode [%d] (execution interrupted)", fop[eip].opcode);
            break;
      }
    
      /* autoincrement the instruction pointer */
      eip++;
   }
   
   FILTERS_UNLOCK;

   return 0;
}

/*
 * pass a packet through every (enabled) filter loaded
 */
void filter_packet(struct packet_object *po) {
   struct filter_list **l;
   for (l = EC_GBL_FILTERS; *l != NULL; l = &(*l)->next) {
      /* if a script drops the packet, do not present it to following scripts */
      if ( po->flags & PO_DROPPED )
         break;
      /* check whether the filter script is enabled */
      if ((*l)->enabled)
         filter_engine((*l)->env.chain, po);
   }
}

/* 
 * execute a function.
 * return FLAG_TRUE if the function was successful
 */
static int execute_func(struct filter_op *fop, struct packet_object *po)
{
   switch (fop->op.func.op) {
      case FFUNC_SEARCH:
         /* search the string */
         if (func_search(fop, po) == E_SUCCESS)
            return FLAG_TRUE;
         break;
         
      case FFUNC_REGEX:
         /* search the string with a regex */
         if (func_regex(fop, po) == E_SUCCESS)
            return FLAG_TRUE;
         break;
         
      case FFUNC_PCRE:
         /* evaluate a perl regex */
         if (func_pcre(fop, po) == E_SUCCESS)
            return FLAG_TRUE;
         break;

      case FFUNC_REPLACE:
         /* replace the string */
         if (func_replace(fop, po) == E_SUCCESS)
            return FLAG_TRUE;
         break;
         
      case FFUNC_INJECT:
         /* replace the string */
         if (func_inject(fop, po) == E_SUCCESS)
            return FLAG_TRUE;
         break;

      case FFUNC_EXECINJECT:
         /* replace the string through output of a executable */
         if (func_execinject(fop, po) == E_SUCCESS)
            return FLAG_TRUE;
         break;
         
      case FFUNC_EXECREPLACE:
         /* replace the string through output of a executable */
         if (func_execreplace(fop, po) == E_SUCCESS)
            return FLAG_TRUE;
         break;
         
      case FFUNC_RANDOM:
         /* replace the string through output of a executable */
         if (func_random(fop, po) == E_SUCCESS)
            return FLAG_TRUE;
         break;

      case FFUNC_LOG:
         /* log the packet */
         if (func_log(fop, po) == E_SUCCESS)
            return FLAG_TRUE;
         break;
         
      case FFUNC_DROP:
         /* drop the packet */
         func_drop(po);
         return FLAG_TRUE;
         break;
         
      case FFUNC_KILL:
         /* kill the connection */
         func_kill(po);
         return FLAG_TRUE;
         break;
         
      case FFUNC_MSG:
         /* display the message to the user */
         USER_MSG("%s\n", fop->op.func.string);
         return FLAG_TRUE;
         break;
         
      case FFUNC_EXEC:
         /* execute the command */
         if (func_exec(fop) == E_SUCCESS)
            return FLAG_TRUE;
         break;
         
      default:
         JIT_FAULT("unsupported function [%d]", fop->op.func.op);
         break;
   }

   return FLAG_FALSE;
}

/* 
 * execute a test.
 * return FLAG_TRUE if the test was successful
 */
static int execute_test(struct filter_op *fop, struct packet_object *po)
{
   /* initialize to the beginning of the packet */
   u_char *base = po->L2.header;
   int (*cmp_func)(u_int32, u_int32) = &cmp_eq;

   /* 
    * point to the right base.
    * if the test is L3.ttl, we have to start from
    * L3.header to count for offset.
    */
   switch (fop->op.test.level) {
      case 2:
         base = po->L2.header;
         break;
      case 3:
         base = po->L3.header;
         break;
      case 4:
         base = po->L4.header;
         break;
      case 5:
         base = po->DATA.data;
         break;
      case 6:
         base = po->DATA.disp_data;
         break;
      default:
         JIT_FAULT("unsupported test level [%d]", fop->op.test.level);
         break;
   }

   /* set the pointer to the comparison function */
   switch(fop->op.test.op) {
      case FTEST_EQ:
         cmp_func = &cmp_eq;
         break;
      case FTEST_NEQ:
         cmp_func = &cmp_neq;
         break;
      case FTEST_LT:
         cmp_func = &cmp_lt;
         break;
      case FTEST_GT:
         cmp_func = &cmp_gt;
         break;
      case FTEST_LEQ:
         cmp_func = &cmp_leq;
         break;
      case FTEST_GEQ:
         cmp_func = &cmp_geq;
         break;
      default:
         JIT_FAULT("unsupported test operation");
         break;
           
   }
   
   /* 
    * get the value with the proper size.
    * 0 is a special case for strings (even binary) 
    */
   switch (fop->op.test.size) {
      case 0:
         /* string comparison */
         if (cmp_func(memcmp(base + fop->op.test.offset, fop->op.test.string, fop->op.test.slen), 0) )
            return FLAG_TRUE;
         break;
      case 1:
         /* char comparison */
         if (cmp_func(*(u_int8 *)(base + fop->op.test.offset), (fop->op.test.value & 0xff)) )
            return FLAG_TRUE;
         break;
      case 2:
         /* short int comparison */
         if (cmp_func(htons(*(u_int16 *)(base + fop->op.test.offset)), (fop->op.test.value & 0xffff)) )
            return FLAG_TRUE;
         break;
      case 4:
         /* int comparison */
         if (cmp_func(htonl(*(u_int32 *)(base + fop->op.test.offset)), (fop->op.test.value & 0xffffffff)) )
            return FLAG_TRUE;
         break;
      case 16: /* well IPv6 addresses should be handled as 16-byte pointer */
         if (cmp_func(memcmp(base + fop->op.test.offset, fop->op.test.ipaddr, fop->op.test.size), 0) )
            return FLAG_TRUE;
         break;
      default:
         JIT_FAULT("unsupported test size [%d]", fop->op.test.size);
         break;
   }
         
   return FLAG_FALSE;
}

/* 
 * make an assignment.
 */
static int execute_assign(struct filter_op *fop, struct packet_object *po)
{
   /* initialize to the beginning of the packet */
   u_char *base = po->L2.header;

   /* check the offensiveness */
   if (EC_GBL_OPTIONS->unoffensive)
      JIT_FAULT("Cannot modify packets in unoffensive mode");
   
   DEBUG_MSG("filter engine: execute_assign: L%d O%d S%d", fop->op.assign.level, fop->op.assign.offset, fop->op.assign.size);
   
   /* 
    * point to the right base.
    */
   switch (fop->op.assign.level) {
      case 2:
         base = po->L2.header;
         break;
      case 3:
         base = po->L3.header;
         break;
      case 4:
         base = po->L4.header;
         break;
      case 5:
         base = po->DATA.data;
         break;
      default:
         JIT_FAULT("unsupported assignment level [%d]", fop->op.assign.level);
         break;
   }

   /* 
    * get the value with the proper size.
    * 0 is a special case for strings (even binary) 
    */
   switch (fop->op.assign.size) {
      case 0:
         memcpy(base + fop->op.assign.offset, fop->op.assign.string, fop->op.assign.slen);
         break;
      case 1:
         *(u_int8 *)(base + fop->op.assign.offset) = (fop->op.assign.value & 0xff);
         break;
      case 2:
         *(u_int16 *)(base + fop->op.assign.offset) = ntohs(fop->op.assign.value & 0xffff); 
         break;
      case 4:
         *(u_int32 *)(base + fop->op.assign.offset) = ntohl(fop->op.assign.value & 0xffffffff);
         break;
      default:
         JIT_FAULT("unsupported assign size [%d]", fop->op.assign.size);
         break;
   }
      
   /* mark the packet as modified */
   po->flags |= PO_MODIFIED;

   return FLAG_TRUE;
}

/* 
 * make an increment or decrement.
 */
static int execute_incdec(struct filter_op *fop, struct packet_object *po)
{
   /* initialize to the beginning of the packet */
   u_char *base = po->L2.header;

   /* check the offensiveness */
   if (EC_GBL_OPTIONS->unoffensive)
      JIT_FAULT("Cannot modify packets in unoffensive mode");
   
   DEBUG_MSG("filter engine: execute_incdec: L%d O%d S%d", fop->op.assign.level, fop->op.assign.offset, fop->op.assign.size);
   
   /* 
    * point to the right base.
    */
   switch (fop->op.assign.level) {
      case 2:
         base = po->L2.header;
         break;
      case 3:
         base = po->L3.header;
         break;
      case 4:
         base = po->L4.header;
         break;
      case 5:
         base = po->DATA.data;
         break;
      default:
         JIT_FAULT("unsupported inc/dec level [%d]", fop->op.assign.level);
         break;
   }

   /* 
    * inc/dec the value with the proper size.
    */
   switch (fop->op.assign.size) {
      case 1:
         if (fop->opcode == FOP_INC) 
            *(u_int8 *)(base + fop->op.assign.offset) += (fop->op.assign.value & 0xff);
         else
            *(u_int8 *)(base + fop->op.assign.offset) -= (fop->op.assign.value & 0xff);
         break;
      case 2:
         if (fop->opcode == FOP_INC) 
            *(u_int16 *)(base + fop->op.assign.offset) += ntohs(fop->op.assign.value & 0xffff); 
         else
            *(u_int16 *)(base + fop->op.assign.offset) -= ntohs(fop->op.assign.value & 0xffff); 
         break;
      case 4:
         if (fop->opcode == FOP_INC) 
            *(u_int32 *)(base + fop->op.assign.offset) += ntohl(fop->op.assign.value & 0xffffffff);
         else
            *(u_int32 *)(base + fop->op.assign.offset) -= ntohl(fop->op.assign.value & 0xffffffff);
         break;
      default:
         JIT_FAULT("unsupported inc/dec size [%d]", fop->op.assign.size);
         break;
   }
      
   /* mark the packet as modified */
   po->flags |= PO_MODIFIED;

   return FLAG_TRUE;
}


/*
 * search a string and return TRUE if found
 */
static int func_search(struct filter_op *fop, struct packet_object *po)
{
   switch (fop->op.func.level) {
      case 5:
         /* search in the real packet */
         if (memmem(po->DATA.data, po->DATA.len, fop->op.func.string, fop->op.func.slen))
            return E_SUCCESS;
         break;
      case 6:
         /* search in the decoded/decrypted packet */
         if (memmem(po->DATA.disp_data, po->DATA.disp_len, fop->op.func.string, fop->op.func.slen))
            return E_SUCCESS;
         break;
      default:
         JIT_FAULT("unsupported search level [%d]", fop->op.func.level);
         break;
   }
   
   return -E_NOTFOUND;
}

/*
 * search a string with a regex and return TRUE if found
 */
static int func_regex(struct filter_op *fop, struct packet_object *po)
{
   switch (fop->op.func.level) {
      case 5:
         /* search in the real packet */
         if (regexec(fop->op.func.ropt->regex, (const char*)po->DATA.data, 0, NULL, 0) == 0)
            return E_SUCCESS;
         break;
      case 6:
         /* search in the decoded/decrypted packet */
         if (regexec(fop->op.func.ropt->regex, (const char*)po->DATA.disp_data, 0, NULL, 0) == 0)
            return E_SUCCESS;
         break;
      default:
         JIT_FAULT("unsupported regex level [%d]", fop->op.func.level);
         break;
   }

   return -E_NOTFOUND;
}


/*
 * evaluate a perl regex and return TRUE if found
 */
static int func_pcre(struct filter_op *fop, struct packet_object *po)
{
#if !defined HAVE_PCRE && !defined HAVE_PCRE2
   (void) fop;
   (void) po;
   JIT_FAULT("pcre_regex support not compiled in ettercap");
   return -E_NOTFOUND;
#else
#ifdef HAVE_PCRE2
   PCRE2_SIZE *ovec;
   pcre2_match_data *match_data;
#else
   int ovec[PCRE_OVEC_SIZE];
   memset(&ovec, 0, sizeof(ovec));
#endif
   int ret;
   
   DEBUG_MSG("filter engine: func_pcre");
   
   switch (fop->op.func.level) {
      case 5:
         
         /* search in the real packet */
#ifdef HAVE_PCRE2
         match_data = pcre2_match_data_create_from_pattern(fop->op.func.ropt->pregex, NULL);
         if ( (ret = pcre2_match(fop->op.func.ropt->pregex, (PCRE2_SPTR)po->DATA.data, po->DATA.len, 0, 0, match_data, NULL)) < 0)
#else
         if ( (ret = pcre_exec(fop->op.func.ropt->pregex, fop->op.func.ropt->preg_extra, po->DATA.data, po->DATA.len, 0, 0, ovec, sizeof(ovec) / sizeof(*ovec))) < 0)
#endif
            return -E_NOTFOUND;

#ifdef HAVE_PCRE2
         pcre2_match_data_free(match_data);
#endif

         /* the pcre wants to modify the packet */
         if (fop->op.func.replace) {
            u_char *replaced;
            u_char *q = fop->op.func.replace;
            size_t i;
            int slen = 0;

            /* don't modify if in unoffensive mode */
            if (EC_GBL_OPTIONS->unoffensive)
               JIT_FAULT("Cannot modify packets in unoffensive mode");
             
            /* 
             * worst case: the resulting string will need:
             *   (n * |input|) + |subst| bytes
             * where
             *   |input| is the length of the matched input string (not the regex!)
             *   |subst| is the length of the substition string
             *   n       is the number of replacement markers in subst
             *
             * therefore, we need to count the number of $ characters first
             * to get an upper limit of the buffer space needed
             */
            int markers = 0;
            for (i=0; q[i]; i++) {
               if (q[i] == '$') markers++;
            }
            /* now: i = strlen(q) */

#ifdef HAVE_PCRE2
            ovec = pcre2_get_ovector_pointer(match_data);
#endif
            SAFE_CALLOC(replaced, markers*(ovec[1]-ovec[0]) + i + 1, sizeof(char));
          
            po->flags |= PO_MODIFIED;

            /* make the replacement */
            uint8_t escaped = 0;
            for (i = 0; i < fop->op.func.rlen; i++) {
               /* we encounter an escape character (\), so the next character is to be taken literally */
               if (!escaped && q[i] == '\\') {
                  escaped = 1;
               }
               /* there is an unescaped position marker */
               else if (!escaped && q[i] == '$') {
                  /* a marker is succeeded by an integer, make sure it is there */
                  if (q[i+1] == '\0')
                     JIT_FAULT("Incomplete marker at end of substitution string");

                  /* so now we can safely move on to the next character, our digit */
                  i++;
                  /* we only support up to 9 markers since we only parse a single digit */
                  if (q[i] < '0' || q[i] > '9')
                     JIT_FAULT("Incomplete marker without integer in substitution string");

                  int marker = q[i]-'0';

                  /* check if the requested marker was found in the pce */
                  if (marker > ret - 1 || marker == 0)
                     JIT_FAULT("Too many marker for this pcre expression");

                  int t = ovec[marker * 2];
                  int r = ovec[marker * 2 + 1];

                  /* copy the sub-string in place of the marker */
                  for ( ; t < r; t++) 
                     replaced[slen++] = po->DATA.data[t];

               }
               /* anything else either has no special meaning or is escaped,
                * so we just copy it
                */
               else {
                  replaced[slen++] = q[i];
                  escaped = 0;
               }
            }
            
            /* calculate the delta */
            int delta = (ovec[0]-ovec[1])+slen;

            /* check if we are overflowing pcap buffer */
            BUG_IF(po->DATA.data < po->packet);
            BUG_IF((u_int16)(EC_GBL_PCAP->snaplen - (po->DATA.data - po->packet)) <= po->DATA.len+delta);

            /* if the substitution string has a different length than the
             * matched original string, we have to move around some data
             */
            int size_left = po->DATA.len - ovec[0] - slen;
            int data_left = po->DATA.len - ovec[1];
#ifdef HAVE_PCRE2
            DEBUG_MSG("func_pcre: match from %ld to %ld, substitution length is %d\n", ovec[0], ovec[1], slen);
#else
            DEBUG_MSG("func_pcre: match from %d to %d, substitution length is %d\n", ovec[0], ovec[1], slen);
#endif
            DEBUG_MSG("func_pcre: packet size changed by %d bytes\n", delta);
            if (delta != 0) {
               /* copy everything behind the matched string to the new position */
               memcpy(po->DATA.data+ovec[0]+slen, po->DATA.data + ovec[1], size_left < data_left ? size_left : data_left);
            }

            /* copy the modified buffer on the original packet */
            memcpy(po->DATA.data+ovec[0], replaced, slen);

            po->DATA.delta += delta;
            po->DATA.len += delta;

            SAFE_FREE(replaced);
         }
         
         break;
      case 6:
         /* search in the decoded one */
#ifdef HAVE_PCRE2
         match_data = pcre2_match_data_create_from_pattern(fop->op.func.ropt->pregex, NULL);
         if ( pcre2_match(fop->op.func.ropt->pregex, (PCRE2_SPTR)po->DATA.disp_data, po->DATA.disp_len, 0, 0, match_data, NULL) < 0)
#else
         if ( pcre_exec(fop->op.func.ropt->pregex, fop->op.func.ropt->preg_extra, po->DATA.disp_data, po->DATA.disp_len, 0, 0, NULL, 0) < 0)
#endif
            return -E_NOTFOUND;

#ifdef HAVE_PCRE2
         pcre2_match_data_free(match_data);
#endif
         break;
      default:
         JIT_FAULT("unsupported pcre_regex level [%d]", fop->op.func.level);
         break;
   }

   return E_SUCCESS;
#endif
}


/* 
 * replace a string in the packet object DATA.data
 */
static int func_replace(struct filter_op *fop, struct packet_object *po)
{
   u_int8 *ptr;
   u_int8 *end;
   size_t len;
   size_t slen = fop->op.func.slen;
   size_t rlen = fop->op.func.rlen;
  
   /* check the offensiveness */
   if (EC_GBL_OPTIONS->unoffensive)
      JIT_FAULT("Cannot modify packets in unoffensive mode");
   
   /* check if it exist at least one */
   if (!memmem(po->DATA.data, po->DATA.len, fop->op.func.string, fop->op.func.slen) )
      return -E_NOTFOUND;

   DEBUG_MSG("filter engine: func_replace");

   /* 
    * XXX BIG WARNING:
    * maxlen is EC_GBL_PCAP->snaplen, but we can't 
    * rely on this forever...
    */
   
   /* take the beginning and the end of the data */
   ptr = po->DATA.data;
   end = ptr + po->DATA.len;
   
   /* do the replacement */
   do {
      /* the len of the buffer to be analized */
      len = end - ptr;

      /* search the string */
      ptr = memmem(ptr, len, fop->op.func.string, slen);

      /* string no found, exit */
      if (ptr == NULL)
         break;
	 
      /* update the len */
      len = end - ptr - slen;
      
      /* set the delta */
      po->DATA.delta += rlen - slen;
      po->DATA.len += rlen - slen;
      
      /* check if we are overflowing pcap buffer */
      BUG_IF(po->DATA.data < po->packet);
      BUG_IF((u_int16)(EC_GBL_PCAP->snaplen - (po->DATA.data - po->packet)) <=  po->DATA.len);
      
      /* move the buffer to make room for the replacement string */   
      memmove(ptr + rlen, ptr + slen, len); 
      /* copy the replacemente string */
      memcpy(ptr, fop->op.func.replace, rlen);
      /* move the ptr after the replaced string */
      ptr += rlen; 
      /* adjust the new buffer end */
      end += rlen - slen;
                                                            
      /* mark the packet as modified */
      po->flags |= PO_MODIFIED;

   } while(ptr != NULL && ptr < end);
   
   return E_SUCCESS;
}

/*
 * inject a file into the communication
 */
static int func_inject(struct filter_op *fop, struct packet_object *po)
{
   int fd;
   void *file;
   size_t size, ret;
   
   /* check the offensiveness */
   if (EC_GBL_OPTIONS->unoffensive)
      JIT_FAULT("Cannot inject packets in unoffensive mode");
   

   DEBUG_MSG("filter engine: func_inject %s", fop->op.func.string);
   
   /* open the file */
   if ((fd = open((const char*)fop->op.func.string, O_RDONLY | O_BINARY)) == -1) {
      USER_MSG("filter engine: inject(): File not found (%s)\n", fop->op.func.string);
      return -E_FATAL;
   }

   /* get the size */
   size = lseek(fd, 0, SEEK_END);

   /* load the file in memory */
   SAFE_CALLOC(file, size, sizeof(char));
 
   /* rewind the pointer */
   lseek(fd, 0, SEEK_SET);
   
   ret = read(fd, file, size);
   
   close(fd);

   if (ret != size)
      FATAL_MSG("Cannot read the file into memory");
 
   /* check if we are overflowing pcap buffer */
   if(EC_GBL_PCAP->snaplen - (po->L4.header - (po->packet + po->L2.len) + po->L4.len) <= po->DATA.len + (unsigned)size)
      JIT_FAULT("injected file too long");
         
   /* copy the file into the buffer */
   memcpy(po->DATA.data + po->DATA.len, file, size);

   /* Adjust packet len and delta */
   po->DATA.delta += size;
   po->DATA.len += size;    

   /* mark the packet as modified */
   po->flags |= PO_MODIFIED;
   
   /* unset the flag to be dropped */
   if (po->flags & PO_DROPPED)
      po->flags ^= PO_DROPPED;

   /* close and unmap the file */
   SAFE_FREE(file);
   
   return E_SUCCESS;
}

/*
 * inject output of a executable into the communication
 */
static int func_execinject(struct filter_op *fop, struct packet_object *po)
{
   FILE *pstream = NULL;
   unsigned char *output = NULL;
   size_t n = 0, offset = 0, size = 128;
   unsigned char buf[size];
   
   /* check the offensiveness */
   if (EC_GBL_OPTIONS->unoffensive)
      JIT_FAULT("Cannot inject packets in unoffensive mode");
   

   DEBUG_MSG("filter engine: func_execinject %s", fop->op.func.string);
   
   /* open the pipe */
   if ((pstream = popen((const char*)fop->op.func.string, "r")) == NULL) {
      USER_MSG("filter engine: execinject(): Command not found (%s)\n", fop->op.func.string);
      return -E_FATAL;
   }
   
   while ((n = read(fileno(pstream), buf, size)) != 0) {
      if (output == NULL) {
         SAFE_CALLOC(output, offset+n, sizeof(unsigned char));
      }
      else {
         SAFE_REALLOC(output, sizeof(unsigned char)*(offset+n));
      }

      memcpy(output+offset, buf, n);
      offset += n;
   }
   
   /* close pipe stream */
   pclose(pstream);

   /* check if we are overflowing pcap buffer */
   if(EC_GBL_PCAP->snaplen - (po->L4.header - (po->packet + po->L2.len) + po->L4.len) <= po->DATA.len + (unsigned)offset)
      JIT_FAULT("injected output too long");
         
   /* copy the output into the buffer */
   memcpy(po->DATA.data + po->DATA.len, output, offset);

   /* Adjust packet len and delta */
   po->DATA.delta += offset;
   po->DATA.len += offset;    

   /* mark the packet as modified */
   po->flags |= PO_MODIFIED;
   
   /* unset the flag to be dropped */
   if (po->flags & PO_DROPPED)
      po->flags ^= PO_DROPPED;

   /* free memory */
   SAFE_FREE(output);
   
   return E_SUCCESS;
}

/*
 * replace output of a executable into the communication
 * executable takes communication before any change in input
 */
static int func_execreplace(struct filter_op *fop, struct packet_object *po)
{
   int child_stdout[2], child_stdin[2];
   pid_t child_pid;
   unsigned char *output = NULL;
   size_t n = 0, offset = 0, size = 128;
   unsigned char buf[size];

   /* check the offensiveness */
   if (EC_GBL_OPTIONS->unoffensive)
      JIT_FAULT("Cannot replace packets in unoffensive mode");


   DEBUG_MSG("filter engine: func_execreplace %s", fop->op.func.string);

   /* open the pipe */
   if (pipe(child_stdin) != 0 || pipe(child_stdout) != 0) {
      USER_MSG("filter engine: execreplace(): failed to create pipes\n");
      return -E_FATAL;
   }
   if ((child_pid = fork()) < 0) {
      USER_MSG("filter engine: execreplace(): failed to fork\n");
      return -E_FATAL;
   }
   if (child_pid == 0) {
       char *const argv[] = { "/bin/sh", "-c", fop->op.func.string, NULL };
       if (dup2(child_stdin[0], STDIN_FILENO) == -1 || dup2(child_stdout[1], STDOUT_FILENO) == -1) {
           exit(EXIT_FAILURE);
       }
       close(child_stdin[1]);
       close(child_stdout[0]);

       execv(argv[0], argv);
       exit(EXIT_FAILURE);
   }

   close(child_stdin[0]);
   close(child_stdout[1]);
   /* fill child STDIN with DATA */
   while (offset < po->DATA.len) {
       n = write(child_stdin[1], po->DATA.data + offset, po->DATA.len - offset);
       if (n == (size_t)-1) {
           break;
       }
       offset += n;
   }
   close(child_stdin[1]);

   /* Fill child STDIN with DATA */
   offset = 0;
   while ((n = read(child_stdout[0], buf, size)) != 0) {
      if (output == NULL) {
         SAFE_CALLOC(output, offset+n, sizeof(unsigned char));
      }
      else {
         SAFE_REALLOC(output, sizeof(unsigned char)*(offset+n));
      }

      memcpy(output+offset, buf, n);
      offset += n;
   }
   /* close pipe stream */
   close(child_stdout[0]);

   /* check if we are overflowing pcap buffer */
   if(EC_GBL_PCAP->snaplen - (po->L4.header - (po->packet + po->L2.len) + po->L4.len) <= po->DATA.len + (unsigned)offset)
      JIT_FAULT("replaced output too long");

   /* copy the output into the buffer */
   memcpy(po->DATA.data, output, offset);

   /* Adjust packet len and delta */
   if ((u_int)offset > po->DATA.len) {
      po->DATA.delta += (int)((u_int)offset - po->DATA.len);
   } else {
      po->DATA.delta -= (int)(po->DATA.len - (u_int)offset);
   }
   po->DATA.len = offset;

   /* mark the packet as modified */
   po->flags |= PO_MODIFIED;

   /* unset the flag to be dropped */
   if (po->flags & PO_DROPPED)
      po->flags ^= PO_DROPPED;

   /* free memory */
   SAFE_FREE(output);

   return E_SUCCESS;
}


/*
 * replace data with a random value
 */
static int func_random(struct filter_op *fop, struct packet_object *po)
{
   unsigned char *base, *ptr, *end;
   size_t len = 0, cnt;
   struct timeval seed;
   long int rnd;

   DEBUG_MSG("filter engine: func_random()");

   /* check the offensiveness */
   if (EC_GBL_OPTIONS->unoffensive)
      JIT_FAULT("Cannot replace packets in unoffensive mode");

   /* define base memory and length */
   switch (fop->op.test.level) {
      case 2:
         /* check if packet contains enough data */
         if (po->L2.len < fop->op.test.offset + fop->op.test.size) {
            USER_MSG("filter engine: random(): packet length %d is smaller "
                  "than the offset to be randomized", po->L2.len);
            return -E_FATAL;
         }
         /* set base and length */
         base = po->L2.header + fop->op.test.offset;
         len = fop->op.test.size;
         break;
      case 3:
         /* check if packet contains enough data */
         if (po->L3.len < fop->op.test.offset + fop->op.test.size) {
            USER_MSG("filter engine: random(): packet length %d is smaller "
                  "than the offset to be randomized", po->L3.len);
            return -E_FATAL;
         }
         /* set base and length */
         base = po->L3.header + fop->op.test.offset;
         len = fop->op.test.size;
         break;
      case 4:
         /* check if packet contains enough data */
         if (po->L4.len < fop->op.test.offset + fop->op.test.size) {
            USER_MSG("filter engine: random(): packet length %d is smaller "
                  "than the offset to be randomized", po->L4.len);
            return -E_FATAL;
         }
         /* set base and length */
         base = po->L4.header + fop->op.test.offset;
         len = fop->op.test.size;
         break;
      case 5:
         base = po->DATA.data;
         len = po->DATA.len;
         break;
      case 6:
         base = po->DATA.disp_data;
         len = po->DATA.disp_len;
         break;
      default:
         DEBUG_MSG("filter engine: random(): unsupported level for randomizing");
         break;
   }

   /* check if offset advances the available buffer */
   if (fop->op.func.offset > len) {
      USER_MSG("filter engine: random(): offset %d advances the available"
            " buffer of %d bytes - skipping randomization\n",
            fop->op.func.offset, len);
      return E_SUCCESS;
   }

   /* check if enough bytes are available for ranomization offset */
   if (fop->op.func.offset + fop->op.func.olen > len) {
      DEBUG_MSG("filter engine: random(): too few bytes (%zu) available to be "
            "randomized at offset %d(%zu) - truncating\n", len,
            fop->op.func.offset, fop->op.func.olen);
      fop->op.func.olen = len - fop->op.func.offset;
   }

   /* build random seed */
   gettimeofday(&seed, NULL);
   srandom(seed.tv_sec ^ seed.tv_usec);

   /* build random data */
   ptr = base + fop->op.func.offset;
   end = ptr + fop->op.func.olen;
   while (ptr < end) {
      /* define how much bytes from random number to be used */
      cnt = end - ptr;
      if (cnt > sizeof(RAND_MAX))
         cnt = sizeof(RAND_MAX);
      /* generate random number, replace target and accelerate pointer */
      rnd = random();
      memcpy(ptr, &rnd, cnt);
      ptr += cnt;
   }

   /* mark the packet as modified */
   po->flags |= PO_MODIFIED;

   /* unset the flag to be dropped */
   if (po->flags & PO_DROPPED)
      po->flags ^= PO_DROPPED;

   return E_SUCCESS;

}

/*
 * log the packet to a file
 */
static int func_log(struct filter_op *fop, struct packet_object *po)
{
   int fd;

   DEBUG_MSG("filter engine: func_log");
   
   /* open the file */
   fd = open((const char*)fop->op.func.string, O_CREAT | O_APPEND | O_RDWR | O_BINARY, 0600);
   if (fd == -1) {
      USER_MSG("filter engine: Cannot open file %s\n", fop->op.func.string);
      return -E_FATAL;
   }

   /* which data should I have to log ? */
   switch(fop->op.func.level) {
      case 5:
         if (write(fd, po->DATA.data, po->DATA.len) < 0)
            USER_MSG("filter engine: Cannot write to file...%d\n", errno);
         break;
      case 6:
         if (write(fd, po->DATA.disp_data, po->DATA.disp_len) < 0)
            USER_MSG("filter engine: Cannot write to file...\n");
         break;
      default:
         JIT_FAULT("unsupported log level [%d]", fop->op.func.level);
         break;
   }

   /* close the file */
   close(fd);
   
   return E_SUCCESS;
}

/*
 * drop the packet
 */
static int func_drop(struct packet_object *po)
{
   DEBUG_MSG("filter engine: func_drop");
   
   /* se the flag to be dropped */
   po->flags |= PO_DROPPED;

   /* the delta is all the payload */
   po->DATA.delta -= po->DATA.len;
   po->DATA.len = 0;
   
   return E_SUCCESS;
}

/*
 * kill the connection:
 *    TCP:  reset
 *    UDP:  icmp port unreachable
 */
static int func_kill(struct packet_object *po)
{
   DEBUG_MSG("filter engine: func_kill");

   if (po->L4.proto == NL_TYPE_TCP) {
      /* reset both sides */
      /* 
       * we can trust the ack number.
       * at least one side will be reset. the other is automatically reset 
       */
      send_tcp(&po->L3.src, &po->L3.dst, po->L4.src, po->L4.dst, po->L4.seq, 0, TH_RST, NULL, 0);
      send_tcp(&po->L3.dst, &po->L3.src, po->L4.dst, po->L4.src, po->L4.ack, 0, TH_RST, NULL, 0);
   } else if (po->L4.proto == NL_TYPE_UDP) {
      send_L3_icmp_unreach(po);
   }
   
   return E_SUCCESS;
}

/*
 * execute the given command
 */
static int func_exec(struct filter_op *fop)
{
   pid_t pid;
   
   DEBUG_MSG("filter engine: func_exec: %s", fop->op.func.string);
   
   /* 
    * the command must be executed by a child.
    * we are forwding packets, and we cannot wait 
    * for the execution of the command 
    */
   pid = fork();
   
   /* check if the fork was successful */
   if (pid == -1)
      SEMIFATAL_ERROR("filter engine: fork() failed, cannot execute %s", fop->op.func.string);
   
   /* differentiate between the parent and the child */
   if (!pid) {
      int k, param_length;
      char **param = NULL;
      char *q = (char*)fop->op.func.string;
      char *p;
      int i = 0;

      /* split the string */
      for (p = strsep(&q, " "); p != NULL; p = strsep(&q, " ")) {
         /* allocate the array */
         SAFE_REALLOC(param, (i + 1) * sizeof(char *));
         
         /* copy the tokens in the array */
         param[i++] = strdup(p); 
      }
      
      /* NULL terminate the array */
      SAFE_REALLOC(param, (i + 1) * sizeof(char *));
      
      param[i] = NULL;
      param_length= i + 1; //because there is a SAFE_REALLOC after the for.
     
      /* 
       * close input, output and error.
       * we don't want to clobber the interface 
       * with output from the child
       */
      close(fileno(stdin));
      close(fileno(stdout));
      close(fileno(stderr));
      
      /* execute the command */
      execve(param[0], param, NULL);

      /* reached on errors */
	   for(k= 0; k < param_length; ++k)
		   SAFE_FREE(param[k]);
	   SAFE_FREE(param);
      _exit(-1);
   }
      
   return E_SUCCESS;
}

/*
 * functions for comparisons
 */
static int cmp_eq(u_int32 a, u_int32 b)
{
   return (a == b);
}

static int cmp_neq(u_int32 a, u_int32 b)
{
   return (a != b);
}

static int cmp_lt(u_int32 a, u_int32 b)
{
   return (a < b);
}

static int cmp_gt(u_int32 a, u_int32 b)
{
   return (a > b);
}

static int cmp_leq(u_int32 a, u_int32 b)
{
   return (a <= b);
}

static int cmp_geq(u_int32 a, u_int32 b)
{
   return (a >= b);
}

/*
 * load the filter from a file 
 */
int filter_load_file(const char *filename, struct filter_list **list, uint8_t enabled)
{
   int fd;
   void *file;
   size_t size, ret;
   struct filter_env *fenv;
   struct filter_header fh;

   DEBUG_MSG("filter_load_file (%s)", filename);

  /* open the file */
   if ((fd = open(filename, O_RDONLY | O_BINARY)) == -1)
      FATAL_MSG("File not found or permission denied");

   /* read the header */
   if (read(fd, &fh, sizeof(struct filter_header)) != sizeof(struct filter_header))
      FATAL_MSG("The file is corrupted");

   /* sanity checks */
   if (fh.magic != htons(EC_FILTER_MAGIC))
      FATAL_MSG("Bad magic in filter file\n"
            "Make sure to compile the filter with a current version of etterfilter");

   /* check pointer alignment */
   if (fh.code % 8)
      FATAL_MSG("Bad instruction pointer alignment\n"
            "Make sure to compile the filter with a current version of etterfilter");
  
   /* which version has compiled the filter ? */
   if (strcmp(fh.version, EC_VERSION))
      FATAL_MSG("Filter compiled for a different version");
   
   /* get the size */
   size = lseek(fd, 0, SEEK_END);

   /* load the file in memory */
   SAFE_CALLOC(file, size, sizeof(char));
 
   /* rewind the pointer */
   lseek(fd, 0, SEEK_SET);
   
   ret = read(fd, file, size);
   
   close(fd);
   
   if (ret != size)
      FATAL_MSG("Cannot read the file into memory");

   FILTERS_LOCK;

   /* advance to the end of the filter list */
   while (*list) list = &(*list)->next;

   /* allocate memory for the list entry */
   SAFE_CALLOC(*list, 1, sizeof(struct filter_list));
   fenv = &(*list)->env;
   
   /* set the global variables */
   fenv->map = file;
   fenv->chain = (struct filter_op *)(file + fh.code);
   fenv->len = size - sizeof(struct filter_header) - fh.code;

   /* 
    * adjust all the string pointers 
    * they must point to the data segment
    */
   reconstruct_strings(fenv, &fh);

   /* save the name of the loaded filter */
   (*list)->name = strdup(filename);

   /* enable the filter if requested */
   (*list)->enabled = enabled;

   FILTERS_UNLOCK;

   /* compile the regex to speed up the matching */
   if (compile_regex(fenv) != E_SUCCESS)
      return -E_FATAL;
   
   USER_MSG("Content filters loaded from %s...\n", filename);
   
   return E_SUCCESS;
}

/* 
 * unload a filter list entry
 */
void filter_unload(struct filter_list **list)
{
   if (*list == NULL) return;

   FILTERS_LOCK;

   struct filter_env *fenv= &(*list)->env;
   size_t i = 0;
   struct filter_op *fop = fenv->chain;
   
   DEBUG_MSG("filter_unload");

   /* free the memory alloc'd for regex */
   while (fop != NULL && i < (fenv->len / sizeof(struct filter_op)) ) {
      /* search for func regex and pcre */
      if(fop[i].opcode == FOP_FUNC) {
         switch(fop[i].op.func.op) {
            case FFUNC_REGEX:
               regfree(fop[i].op.func.ropt->regex);
               SAFE_FREE(fop[i].op.func.ropt);
               break;
               
            case FFUNC_PCRE:
               #ifdef HAVE_PCRE2
                  pcre2_code_free(fop[i].op.func.ropt->pregex);
                  SAFE_FREE(fop[i].op.func.ropt);
               #else
                  pcre_free(fop[i].op.func.ropt->pregex);
                  pcre_free(fop[i].op.func.ropt->preg_extra);
                  SAFE_FREE(fop[i].op.func.ropt);
               #endif               
               break;
         }
      }
      i++;
   }
   
   /* free the memory region containing the file */
   SAFE_FREE(fenv->map);

   /* wipe the pointer */
   fenv->map = NULL;
   fenv->chain = NULL;
   fenv->len = 0;

   SAFE_FREE((*list)->name);

   /* reclose the filter list */
   struct filter_list **ptr = list;
   struct filter_list *succ = (*list)->next;
   *ptr = succ;
   SAFE_FREE(*list);

   FILTERS_UNLOCK;
}

void filter_clear(void) {
   FILTERS_LOCK;
   struct filter_list **l = EC_GBL_FILTERS;
   while (*l) {
      filter_unload(l);
   }
   FILTERS_UNLOCK;
}


/*
 * replace relative offset to real address in the strings fields
 */
static void reconstruct_strings(struct filter_env *fenv, struct filter_header *fh)
{
   size_t i = 0;
   struct filter_op *fop = fenv->chain;
     
   /* parse all the instruction */ 
   while (i < (fenv->len / sizeof(struct filter_op)) ) {
         
      /* 
       * the real address for a string is the base of the mmap'd file
       * plus the base of the data segment plus the offset in the field
       */
      switch(fop[i].opcode) {
         case FOP_FUNC:
            if (fop[i].op.func.slen)
               fop[i].op.func.string = (fenv->map + fh->data + (size_t)fop[i].op.func.string);
            if (fop[i].op.func.rlen)
               fop[i].op.func.replace = (fenv->map + fh->data + (size_t)fop[i].op.func.replace);
            break;
            
         case FOP_TEST:
            if (fop[i].op.test.slen)
               fop[i].op.test.string = (fenv->map + fh->data + (size_t)fop[i].op.test.string);
            break;
         
         case FOP_ASSIGN:
            if (fop[i].op.assign.slen)
               fop[i].op.assign.string = (fenv->map + fh->data + (size_t)fop[i].op.assign.string);
            break;
      }
      
      i++;
   }  
   
}

/*
 * compile the regex of a filter_op
 */
static int compile_regex(struct filter_env *fenv)
{
   size_t i = 0;
   struct filter_op *fop = fenv->chain;
   char errbuf[100];
#ifdef HAVE_PCRE2
   int perrbuf;
   PCRE2_SIZE err;
#else
   const char *perrbuf = NULL;
   int err;
#endif
     
   /* parse all the instruction */ 
   while (i < (fenv->len / sizeof(struct filter_op)) ) {
      
      /* search for func regex and pcre */
      if(fop[i].opcode == FOP_FUNC) {
         switch(fop[i].op.func.op) {
            case FFUNC_REGEX:

               /* alloc the structures */
               SAFE_CALLOC(fop[i].op.func.ropt, 1, sizeof(struct regex_opt));
               SAFE_CALLOC(fop[i].op.func.ropt->regex, 1, sizeof(regex_t));
   
               /* prepare the regex */
               err = regcomp(fop[i].op.func.ropt->regex, (const char*)fop[i].op.func.string, REG_EXTENDED | REG_NOSUB | REG_ICASE );
               if (err) {
                  regerror(err, fop[i].op.func.ropt->regex, errbuf, sizeof(errbuf));
                  FATAL_MSG("filter engine: %s", errbuf);
               } 
               break;
               
            case FFUNC_PCRE:
               #ifdef HAVE_PCRE2

               /* alloc the structure */
               SAFE_CALLOC(fop[i].op.func.ropt, 1, sizeof(struct regex_opt));

               /* prepare the regex (with default option) */
               fop[i].op.func.ropt->pregex = pcre2_compile(fop[i].op.func.string, PCRE2_ZERO_TERMINATED,
                     0, &perrbuf, &err, NULL );
               if (fop[i].op.func.ropt->pregex == NULL)
               {
                  PCRE2_UCHAR buffer[256];
                  pcre2_get_error_message(err, buffer, sizeof(buffer));
                  FATAL_MSG("filter engine: %s\n", buffer);
               }

               #else

               /* alloc the structure */
               SAFE_CALLOC(fop[i].op.func.ropt, 1, sizeof(struct regex_opt));

               /* prepare the regex (with default option) */
               fop[i].op.func.ropt->pregex = pcre_compile(fop[i].op.func.string, 0, &perrbuf, &err, NULL );
               if (fop[i].op.func.ropt->pregex == NULL)
                  FATAL_MSG("filter engine: %s\n", perrbuf);
  
               /* optimize the pcre */
               fop[i].op.func.ropt->preg_extra = pcre_study(fop[i].op.func.ropt->pregex, 0, &perrbuf);
               if (perrbuf != NULL)
                  FATAL_MSG("filter engine: %s\n", perrbuf);
               
               #endif               
               break;
         }
      }
      i++;
   } 

   return E_SUCCESS;
}

/*
 * Walk the list of loaded filters and call the callback function
 * for every single list item along with the argument passed.
 * The callback function can stop the list from being traversed
 * any further by returning a false value.
 */
void filter_walk_list( int(*cb)(struct filter_list*, void*), void *arg) {
   struct filter_list **l;
   FILTERS_LOCK;
   for (l = EC_GBL_FILTERS; *l != NULL; l = &(*l)->next) {
      /* do not traverse the list any further if the callback tells us so */
      if (!cb(*l, arg))
         break;
   }
   FILTERS_UNLOCK;
}

/* EOF */

// vim:ts=3:expandtab

