/*
    ettercap -- packet object handling

    Copyright (C) 2001  ALoR <alor@users.sourceforge.net>, NaGA <crwm@freemail.it>

    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.

    $Id: ec_packet.c,v 1.5 2002/05/04 09:38:21 alor Exp $
*/

#include "include/ec_main.h"

#include "include/ec_packet.h"
#include "include/ec_dissector.h"
#include "include/ec_inet_structures.h"

/* protos... */

int packet_create_object(struct packet_object **po, u_char *buf, int *len);
int packet_destroy_object(struct packet_object **po);
int packet_duplicate(struct packet_object *po, char level, u_char **buf);

/* --------------------------- */

int packet_create_object(struct packet_object **po, u_char *buf, int *len)
{

   ETH_header *eth;
   IP_header *ip = NULL;

   *po = (struct packet_object *)calloc(1, sizeof(struct packet_object));
   if (*po == NULL)
      ERROR_MSG("calloc()");

/* ETHERNET level */

   eth = (ETH_header *) buf;
   (*po)->L2.header = buf;
   (*po)->L2.len = *len;

   (*po)->len = len; /* global length */
   
   (*po)->L3.header = (u_char *) (eth + 1);     /* default */

/* ARP level */

   if (ntohs(eth->type) == ETH_P_ARP ) {

      ARP_header *arp;

      arp = (ARP_header *)(eth + 1);
      (*po)->L3.proto = ntohs(eth->type);
      (*po)->L3.header = (u_char *) arp;
      (*po)->L3.options = NULL;
      (*po)->L3.len = sizeof(ARP_header);
   }

/* IP level */

   if (ntohs(eth->type) == ETH_P_IP ) {

      ip = (IP_header *)(eth + 1);
      
      (*po)->L3.proto = ntohs(eth->type);
      (*po)->L3.header = (u_char *) ip;

      if ( (ip->h_len * 4) != IP_HEADER )    /* there are options */
         (*po)->L3.options = (u_char *)(ip + 1);

      (*po)->L3.len = ntohs(ip->t_len) ;
      
      if ( (*po)->L3.len > *len - ETH_HEADER) {    /* unexpected len */
         free(*po);
         *po = NULL;
         return -1;
      }
   }

   (*po)->L4.header = (u_char *) ((int)ip + ip->h_len * 4);    /* default */

/* ICMP level */

   if (ip && ip->proto == IPPROTO_ICMP) {

      ICMP_header *icmp;

      icmp = (ICMP_header *) ((int)ip + ip->h_len * 4);
      
      (*po)->L4.proto = ip->proto;
      (*po)->L4.header = (u_char *) icmp;
      (*po)->L4.options = NULL;
      (*po)->L4.len = ntohs(ip->t_len) - (ip->h_len * 4);
   }

/* UDP level */

   if (ip && ip->proto == IPPROTO_UDP) {

      UDP_header *udp;

      udp = (UDP_header *) ((int)ip + ip->h_len * 4);
      
      (*po)->L4.proto = ip->proto;
      (*po)->L4.header = (u_char *) udp;
      (*po)->L4.options = NULL;
      (*po)->L4.len = ntohs(udp->len);
      (*po)->L4.sport = &udp->source;
      (*po)->L4.dport = &udp->dest;

      if ((*po)->L4.len > *len - ETH_HEADER - IP_HEADER) {    /* unexpected len */
         free(*po);
         *po = NULL;
         return -1;
      }
      
      /* DATA level */

      (*po)->data.data = (char *)((int)udp + UDP_HEADER);
      (*po)->data.len = ntohs(udp->len) - UDP_HEADER;

   }

/* TCP level */

   if (ip && ip->proto == IPPROTO_TCP) {

      TCP_header *tcp;

      tcp = (TCP_header *) ((int)ip + ip->h_len * 4);
      
      (*po)->L4.proto = ip->proto;
      (*po)->L4.header = (u_char *) tcp;
      (*po)->L4.sport = &tcp->source;
      (*po)->L4.dport = &tcp->dest;

      if ( (tcp->doff * 4) != TCP_HEADER )      /* there are options */
         (*po)->L4.options = (u_char *)(tcp + 1);

      (*po)->L4.len = ntohs(ip->t_len) - (ip->h_len * 4);

      if ((*po)->L4.len > *len - ETH_HEADER - IP_HEADER) {    /* unexpected len */
         free(*po);
         *po = NULL;
         return -1;
      }
      
      /* DATA level */

      (*po)->data.data = (char *)((int)tcp + tcp->doff * 4);
      (*po)->data.len = (*po)->L4.len - (tcp->doff * 4);

   }

/* dissector */

   Dissector_SetDissector(*po);

/* filter */

   // (*po)->filter =


   return (0);
}


int packet_destroy_object(struct packet_object **po)
{

   if (*po) {
      free(*po);
      *po = NULL;
   }

   return 0;

}


int packet_duplicate(struct packet_object *po, char level, u_char **buf)
{

   int len = 0;
   u_char * pobuf = NULL;

   switch (level & LEVEL_MASK ) {
         case LEVEL_2:
            len = po->L2.len;
            pobuf = po->L2.header;
            break;
         case LEVEL_3:
            len = po->L3.len;
            pobuf = po->L3.header;
            break;
         case LEVEL_4:
            len = po->L4.len;
            pobuf = po->L4.header;
            break;
         case LEVEL_DATA:
            len = po->data.len;
            pobuf = po->data.data;
            break;
         default:
            ERROR_MSG("incorrect level specified");
            break;
   }

   if (level & DUP_ALLOC) {
      *buf = (u_char *)calloc(len, sizeof(u_char));
      if (*buf == NULL)
         ERROR_MSG("calloc()");
   } 

   memcpy(*buf, pobuf, len);

   return len;
}

/* EOF */

// vim:ts=3:expandtab
