/*
    ettercap -- pcap file utilities, read and dump file in tcpdump format

    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_pcapfile.c,v 1.8 2003/06/09 13:42:00 lordnaga Exp $
*/

#include "include/ec_main.h"

#include "include/ec_inet_structures.h"

#define  SWAPLONG(y) ((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff))
#define  SWAPSHORT(y) ( (((y)&0xff)<<8) | ((u_int16)((y)&0xff00)>>8) )

#define DLT_EN10MB   1   /* Ethernet (10Mb) */
#define W802_11      105 /* Wireless */ 
#define ETH_ADDR_LEN 6

   struct pcap_file_header {
      u_int32 magic;
         #define TCPDUMP_MAGIC 0xa1b2c3d4
         #define PATCHED_TCPDUMP_MAGIC 0xa1b2cd34
      u_int16 version_major;
         #define PCAP_VERSION_MAJOR 2
      u_int16 version_minor;
         #define PCAP_VERSION_MINOR 4
      int thiszone;     /* gmt to local correction */
      u_int32 sigfigs;    /* accuracy of timestamps */
      u_int32 snaplen;    /* max length saved portion of each pkt */
      u_int32 linktype;   /* data link type (LINKTYPE_*) */
   };

   struct pcap_pkthdr {
      struct timeval ts;   /* time stamp */
      u_int32 caplen;        /* length of portion present */
      u_int32 len;           /* length this packet (off wire) */
   };

   struct wifi_header {
      u_int16  type;
#define WIFI_DATA_IN    0x0802
#define WIFI_DATA_OUT   0x0801
#define WIFI_BACON      0x0800
      u_int16  duration;
      u_int8   dha[ETH_ADDR_LEN];
      u_int8   sha[ETH_ADDR_LEN];
      u_int8   bssid[ETH_ADDR_LEN];
      u_int16  seq;
   };

   struct wifi_ll_header {
      u_int8   dsap;
      u_int8   ssap;
      u_int8   control;
      u_int8   organization[3];
      u_int16  type;
   };
   

char read_from_dump_flag = 0; // to be cleared by inteface
char write_to_dump_flag = 0; // to be cleared by inteface

// protos

int Pcapfile_Read(char *buf, short MTU);
int Pcapfile_Write(char *buf, short len);

//--------------------------------------------------

int Pcapfile_Read(char *buf, short MTU)
{
   static FILE *tfile = NULL;

   static struct pcap_file_header hdr;
   struct pcap_pkthdr pck_hdr;

   size_t pck_len = 0;

   if (!read_from_dump_flag)
   {
      if ((tfile = fopen(Options.dumpfile, "rb")) == NULL)
         Error_msg("Cannot open %s for OFF LINE sniffing !!", Options.dumpfile);

      DEBUG_MSG("OFFLINE sniffing from %s", Options.dumpfile);

      if (fread((char *)&hdr, sizeof(hdr), 1, tfile) != 1)  // read the initial header
         ERROR_MSG("fread()");

      if (hdr.magic != TCPDUMP_MAGIC && hdr.magic != PATCHED_TCPDUMP_MAGIC) {
         hdr.magic = SWAPLONG(hdr.magic);
         if (hdr.magic != TCPDUMP_MAGIC && hdr.magic != PATCHED_TCPDUMP_MAGIC)
            Error_msg("[%s] Bad tcpdump file format !!", Options.dumpfile);

         hdr.version_major = SWAPSHORT(hdr.version_major);
         hdr.version_minor = SWAPSHORT(hdr.version_minor);
         hdr.snaplen = SWAPLONG(hdr.snaplen);
         hdr.linktype = SWAPLONG(hdr.linktype);
      }

      DEBUG_MSG("PCAP MAGIC    : %#010X", hdr.magic);
      DEBUG_MSG("PCAP VERSION  : %d.%d ", hdr.version_major, hdr.version_minor);
      DEBUG_MSG("PCAP SNAPLEN  : %#010X", hdr.snaplen);
      DEBUG_MSG("PCAP LINKTYPE : %d [%d]", hdr.linktype, DLT_EN10MB);


      if (hdr.linktype != DLT_EN10MB && hdr.linktype != W802_11) 
         Error_msg("[%s] Ettercap can only sniff from ethernet or wi-fi dump file !! [Type %d]", Options.dumpfile,hdr.linktype);

      read_from_dump_flag = 1;
   }


   if (!tfile) {
      return 0;
   }

ec_pcap_retry:
   if (fread((char *)&pck_hdr, sizeof(pck_hdr), 1, tfile) != 1)
   {
      DEBUG_MSG("OFFLINE sniffing closed !!");
      fclose(tfile);
      tfile = NULL;
      return 0;
   }

   pck_len = pck_hdr.caplen;

   if (pck_hdr.caplen != pck_hdr.len) 
      DEBUG_MSG("CAPLEN %d LEN %d", pck_hdr.caplen, pck_hdr.len);

   if (hdr.linktype == DLT_EN10MB)
   {
       if ( pck_len > MTU + ETH_HEADER ) 
       {
           DEBUG_MSG("OUT OF LIMIT pck_len %d", pck_len);
           pck_len = MTU + ETH_HEADER;
       }

       if (fread(buf, pck_len, 1, tfile) != 1)
       {
          DEBUG_MSG("OFFLINE sniffing closed !!");
          fclose(tfile);
          tfile = NULL;
          return 0;
       }
   }
   else /* Wi-Fi */
   {
       struct wifi_header *wfh;
       struct wifi_ll_header *wfhl;
       char *temp_buf;
       
       temp_buf = calloc(pck_len, sizeof(char));
       
       if (fread(temp_buf, pck_len, 1, tfile) != 1)
       {
          DEBUG_MSG("OFFLINE sniffing closed !!");
          fclose(tfile);
	  free(temp_buf);
          tfile = NULL;
          return 0;
       }
       
       wfh = (struct wifi_header *)temp_buf;
       if (ntohs(wfh->type) != WIFI_DATA_IN && ntohs(wfh->type) != WIFI_DATA_OUT)
       {
           free(temp_buf);
	   goto ec_pcap_retry;
       } 
       
       wfhl = (struct wifi_ll_header *)(wfh + 1);
       memcpy(buf, wfh->dha, ETH_ADDR_LEN);
       buf += ETH_ADDR_LEN;
       memcpy(buf, wfh->sha, ETH_ADDR_LEN);
       buf += ETH_ADDR_LEN;
       memcpy(buf, &wfhl->type, 2);
       buf += 2;
       pck_len = pck_len - sizeof(struct wifi_header) - sizeof(struct wifi_ll_header) + ETH_HEADER;
       if ( pck_len > MTU + ETH_HEADER ) 
       {
           DEBUG_MSG("OUT OF LIMIT pck_len %d", pck_len);
           pck_len = MTU + ETH_HEADER;
       }

       memcpy(buf, wfhl+1, pck_len - ETH_HEADER); 
       free(temp_buf);
   }

   
   return pck_len;
}




int Pcapfile_Write(char *buf, short len)
{
   static FILE *tfile = NULL;

   struct pcap_file_header hdr;
   struct pcap_pkthdr pck_hdr;

   if (!write_to_dump_flag)
   {
      if ((tfile = fopen(Options.dumpfile, "wb")) == NULL)
         Error_msg("Cannot open %s for DUMP !!", Options.dumpfile);

      DEBUG_MSG("OFFLINE dumping to %s", Options.dumpfile);

      hdr.magic = TCPDUMP_MAGIC;
      hdr.version_major = PCAP_VERSION_MAJOR;
      hdr.version_minor = PCAP_VERSION_MINOR;

      hdr.thiszone = 0;       // store data in GMT timezone... who cares ?
      hdr.snaplen = 0xFFFF;   // FIXME snaplen;
      hdr.sigfigs = 0;
      hdr.linktype = DLT_EN10MB;

      if (fwrite((char *)&hdr, sizeof(hdr), 1, tfile) != 1)    // write the initial header
         ERROR_MSG("fwrite()");

      DEBUG_MSG("PCAP MAGIC    : %#010X", hdr.magic);
      DEBUG_MSG("PCAP VERSION  : %d.%d ", hdr.version_major, hdr.version_minor);
      DEBUG_MSG("PCAP SNAPLEN  : %#010X", hdr.snaplen);
      DEBUG_MSG("PCAP LINKTYPE : %d [%d]", hdr.linktype, DLT_EN10MB);

      write_to_dump_flag = 1;
   }

   if (write_to_dump_flag == 2) {
      fclose(tfile);
      tfile = NULL;
      write_to_dump_flag = 0;
      return 0;
   }

#if defined(HAVE_GETTIMEOFDAY)
   gettimeofday(&pck_hdr.ts, 0);
#else
   pck_hdr.ts.tv_sec  = 0;    // FIXME : ts.tv_sec;
   pck_hdr.ts.tv_usec = 0;    // FIXME : ts.tv_usec;
#endif

   pck_hdr.caplen     = len;
   pck_hdr.len        = len;

   if (fwrite(&pck_hdr, sizeof(pck_hdr), 1, tfile) != 1)
      ERROR_MSG("fwrite()");

   if (fwrite((char *)buf, pck_hdr.caplen /* len ?? */, 1, tfile) != 1)
      ERROR_MSG("fwrite()");

   return len + sizeof(pck_hdr); // return the byte written in the file
}
