#ifdef TCPDUMP_FED

/*  -*- Mode: C;  -*- */

/******************************************************************************
*                                                                             *
*   Copyright 2005 University of Cambridge Computer Laboratory.               *
*                                                                             *
*   This file is part of Nprobe.                                              *
*                                                                             *
*   Nprobe 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.                                       *
*                                                                             *
*   Nprobe 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 Nprobe; if not, write to the Free Software                     *
*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA *
*                                                                             *
******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <string.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <assert.h>
#ifdef __alpha__
#include <sys/mbuf.h>
#include <machine/endian.h>
#endif

#include <net/route.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/in_systm.h>
#undef __STDC__
#include <netinet/ip.h>
#include <netinet/udp.h>
#ifdef __alpha__
#include <netinet/ip_var.h>
#endif
#ifdef __linux__
#define __FAVOR_BSD
#endif
#include <netinet/tcp.h>
#ifdef __alpha__
#include <net/if_llc.h>
#include <netinet/if_ether.h>
#endif
#include <netinet/if_fddi.h>

#ifdef __linux__
//#include <linux/if_ether.h>
#include <net/ethernet.h>
#include <netinet/ip_icmp.h>

#include "linux_tweaks.h"

#endif


#include "list.h"
#include "pkt.h"
#include "interface.h"
#include "tcpdump_patch.h"
#include "if_nprobe.h"
#include "interface.h"
#include "interesting.h"



#include "list.h"
#include "pkt.h"
#include "interface.h"
#include "tcpdump_patch.h"
#include "if_nprobe.h"
#include "interface.h"
#include "interesting.h"

#include "output.h"

void
swap_hdr(struct file_header *hp)
{
  hp->version_major = SWAPSHORT(hp->version_major);
  hp->version_minor = SWAPSHORT(hp->version_minor);
  hp->thiszone = SWAPLONG(hp->thiszone);
  hp->sigfigs = SWAPLONG(hp->sigfigs);
  hp->snaplen = SWAPLONG(hp->snaplen);
  hp->linktype = SWAPLONG(hp->linktype);
}


void 
open_tcpdumpfile(link_data_t *ld)
{
  int format_err = 0;
  struct file_header hdr;

  if(ld->fnm[0] == '\0') {
      error("open_tcpdumpfile", "weird - a zero length filename");
  }

  if (ld->fnm[0] == '-' && ld->fnm[1] == '\0') 
    {
      ld->inf = stdin;
      ld->fnm = "stdin";
      ld->stdin = 1;
    }
  else
    {
      if ((ld->inf = fopen(ld->fnm, "r")) == NULL)
	error("open_tcpdumpfile", ld->fnm);
    }
  
  clearerr(ld->inf);
	
  if (fread((char *)&hdr, sizeof(hdr), 1, ld->inf) != 1)
    error("open_tcpdumpfile: read header", ld->fnm);

  if (hdr.magic != TCPDUMP_MAGIC)
    {
      if (hdr.magic == TCPDUMP_MAGIC_LINUX)
	{
	  format_err = 0;
	  ld->linux_format = 1;
	}	
      else 
	{
	  int sl = SWAPLONG(hdr.magic);
	  if (sl == TCPDUMP_MAGIC)
	    {
	      format_err = 0;		
	      ld->sf_swapped = 1;
	    }	
	  else if (sl == TCPDUMP_MAGIC_LINUX)
	    {
	      format_err = 0;		
	      ld->sf_swapped = 1;
	      ld->linux_format = 1;
	    }	
	}	
    }	
  else 	
    {	
      format_err = 0;
    }

  if (format_err)
    error("open_tcpdumpfile: bad format", NULL);

  if (ld->sf_swapped)
    swap_hdr(&hdr);

  if (ld->linux_format)
    printf("Yeuchhhh! - Linux collected dump\n");

  if (hdr.version_major < VERSION_MAJOR)
    error("open_tcpdumpfile: bad tcpdump version", NULL);

  //*linktypep = hdr.linktype;
  ld->linktype = hdr.linktype;
  ld->version_minor = hdr.version_minor;
  
#ifdef ZFLAG
  if(hflag)
    {
      printf("Collecting tcpdump: version  %hu.%hu timezone %d sigfigs %u snaplen %u linktype %u\n\n", hdr.version_major, hdr.version_minor, hdr.thiszone, hdr.sigfigs, hdr.snaplen, hdr.linktype);
    }
#endif

  ld->snaplen = hdr.snaplen;
  
  return;
  
}

inline void 
stroke_pcap_hdr(link_data_t *ld, struct linux_packet_header *h)
{
  
  if (ld->sf_swapped) 
    {
      /* these were written in opposite byte order */
      h->cap_len = SWAPLONG(h->cap_len);
      h->len = SWAPLONG(h->len);
      h->ts.tv_sec = SWAPLONG(h->ts.tv_sec);
      h->ts.tv_usec = SWAPLONG(h->ts.tv_usec);
    }

  /* 
   * Courtesy of tcpdump-3.4 + local hack so that we can read stuff 
   * collected on linux boxes 
   */
  /*
   * "We interchanged the caplen and len fields at version 2.3, in order to
   * match the bpf header layout.  But unfortunately some files were written
   * with version 2.3 in their headers but without the interchanged fields."
   */
  
  if (ld->version_minor > 3 || 
      (ld->version_minor == 3 && h->cap_len > h->len) || ld->swap_len) 
    { 
      int t = h->cap_len; 
      h->cap_len = h->len; 
      h->len = t; 
      /* Mark interface as swapping len fields to make reversible */ 
      ld->swap_len = 1;
    }

  return;
}

  


long 
next_pkt(bufrec_t *bp)
{
  struct linux_packet_header h;
  int phlen;
  struct link_data *ldp = bp->link_data;
  FILE *fp = ldp->inf;
  static int pno = 0;
  long curr_offset;
  
  pno += 1;
  if (!ldp->stdin && (curr_offset = ftell(fp)) == -1)
    error("next_pkt: fseek() curr_offset", NULL);
  
  if (ldp->linux_format)
    phlen = sizeof(struct linux_packet_header);
  else 
    phlen = sizeof(struct packet_header);
  
  /* read the stamp */
  if (fread((char *)&h, phlen, 1, fp) != 1) 
    {
      if (feof(fp))
	return EOF;
      else
	error("next_pkt", "header read");
    }

  stroke_pcap_hdr(ldp, &h);

  bp->ts.tv_sec = h.ts.tv_sec;
  bp->ts.tv_usec = h.ts.tv_usec;
  bp->plength = h.cap_len;

  if (h.cap_len > ldp->snaplen)
    {
      /* Bum pcap hdr - attempt recovery */
      return scan(bp, &h);
    }
  
  if (bp->plength == 0)
    error("next_pkt: zero packet length", "");
  
  /* get a buffer for the packet */
  if ((bp->base = (unsigned char *)malloc(bp->plength)) == (unsigned char *)-1)
    error("next_pkt: pkt. buffer", "malloc");
  
  /* read the packet itself */
  
  if (fread((char *)bp->base, bp->plength, 1, fp) != 1)
    {
      if (feof(fp))
	{
	  free(bp->base);
	  return EOF;
	}
      else
	{
	  error("next_pkt", "pkt read");
	}
    }

  ldp->last_offset = curr_offset;

  
  return 0L;

}


/*
 * Attempt to recover from bum pcap hdr: strategy is to scan through the file
 *  byte by byte until what looks like a feasible hdr is found. Test for 
 *  feasability (check_pkt()) is a feasable pcap hdr length, correct IP hdr 
 *  version, and IP hdr datagram length equal to claimed pcap hdr length - or
 *  a good reason why not (eg an ether minimum size segment). 
 * 
 * If reading from a file the preceding pcap hdr is checked in case its 
 *  bogosity has landed us somewhere other than the next header (yes, and that
 *  could be bogus because of a preceding bum header, and so on - but the 
 *  chances are vanishingly small as you work backwards so its probably only
 *  worth checking back one. If reading from stdin can only scan forwards from
 * the current position.
 */

#define PKT_SAMPLE_SZ 40 /* long enough for our checks */

int 
scan(bufrec_t *bp, struct linux_packet_header *hp)
{
  
  unsigned char *buf;
  struct linux_packet_header h;
  int phlen;
  struct link_data *ldp = bp->link_data;
  int linux_format = ldp->linux_format;
  FILE *fp = ldp->inf;
  int snaplen = ldp->snaplen;
  int bufsz;
  int badlen = bp->plength;
  long off;
  int skip = 0;

  /* 
   * Fn is called with the bum hdr in hp and the file read up to the end of
   *  the purported header
   */
  if (linux_format)
    phlen = sizeof(struct linux_packet_header);
  else 
    phlen = sizeof(struct packet_header);

  bufsz = phlen + PKT_SAMPLE_SZ;
  
  if ((buf = (unsigned char *)malloc(snaplen)) == (unsigned char *)-1)
    error("scan(): pkt. buffer", "malloc"); 
  
  if (!ldp->stdin)
    {
      /* not stdin - can rewind to check preceding pcap hdr */
      int last_ok = 0;
      if (fseek(fp, ldp->last_offset, SEEK_SET) == -1)
	error("scan(): first fseek()", NULL);
      
      if (fread((char *)&h, phlen, 1, fp) != 1) 
	error("scan()", "first header read");
      
      stroke_pcap_hdr(ldp, &h);
      if (h.cap_len <= snaplen)
	{
	  bp->base = buf;
	  bp->plength = h.cap_len;
	  if (fread((char *)bp->base, h.cap_len, 1, fp) != 1)
	    error("next_pkt", "pkt read");
	  if (check_pkt(bp))
	    last_ok = 1;
	}
      
      if (!last_ok)
	/* rewind again to scan from preceding (bum) hdr start */
	if (fseek(fp, ldp->last_offset, SEEK_SET) == -1)
	  error("scan(): second fseek()", NULL);
      
      /* now read the purported header into the buffer */
      if (fread((char *)buf, phlen, 1, fp) != 1) 
	error("scan()", "second header read");
	   
      off = ftell(fp);
      if (off == -1)
	error("scan(): ftell()", "");
      else
	off -= phlen;
    } 
  else
    {
      /* not stdin - restore the purported header and get into the buffer */
      stroke_pcap_hdr(ldp, hp);
      memcpy((void *)buf, (void *)hp, phlen);
    }

  /* 
   * now we've got the purported pcap hdr in the buffer and the input 
   * aligned at its end
   */
  
  bp->base = buf + phlen; /* point pkt start at 1st byte beyond pcap hdr */
  
  /* read in enough of the potential packet to check it */
  if (fread((char *)bp->base, PKT_SAMPLE_SZ, 1, fp) != 1)
    {
      if (feof(fp))
	goto eof;
      else
	error("scan()", "pkt read");
    }

  /* now start the scan */
  while (1)
    {
      skip += 1;
      /* shift buffer contents forwards one byte */
      memmove((void *)buf, (void *)buf+1, bufsz-1);
      /* get next byte of input */
      if (fread((char *)buf + bufsz - 1, 1, 1, fp) != 1)
      {
	if (feof(fp))
	  goto eof;
	else
	  error("scan()", "pkt read");
      }
      /* copy purported header into header struct and byteswap as necessary */
      memcpy((void *)&h, (void *)buf, phlen);
      stroke_pcap_hdr(ldp, &h);
      bp->plength = h.cap_len;
      /* see if we've aligned with a feasible hdr and packet */
      if (h.cap_len <= snaplen && h.cap_len >= PKT_SAMPLE_SZ && check_pkt(bp))
	goto gotone;
    }

  

 gotone: 
  ldp->last_offset = off + skip;
  /* now get a buffer for the valid packet */
  if ((bp->base = (unsigned char *)malloc(h.cap_len)) == (unsigned char *)-1)
    error("scan(): pkt. buffer", "malloc"); 
  bp->plength = h.cap_len;
  bp->ts.tv_sec = h.ts.tv_sec;
  bp->ts.tv_usec = h.ts.tv_usec;
  /* get the already read part of the packet into the buffer */
  memcpy(bp->base, buf + phlen, PKT_SAMPLE_SZ);
  /* and read in any remaining bytes of the packet */
  if (h.cap_len > PKT_SAMPLE_SZ)
    {
      if (fread((char *)bp->base + PKT_SAMPLE_SZ, h.cap_len - PKT_SAMPLE_SZ, 1, fp) != 1)
	{
	  if (feof(fp))
	    goto eof;
	  else
	    error("scan()", "pkt read");
	}
    }
  
  free(buf);

  fprintf(stderr, " Bum pcap hdr len %d (snaplen %d)\n file %s - recovered synch after skipping %d bytes\n", badlen, snaplen, ldp->fnm, skip);

  INFORM(" Bum pcap hdr len %d (snaplen %d)\n file %s - recovered synch after skipping %d bytes\n", badlen, snaplen, ldp->fnm, skip);
    
  return 0;
      
    
 eof:

  free(buf);

  fprintf(stderr, " Bum pcap hdr len %d (snaplen %d)\n file %s - EOF encountered after skipping %d bytes\n", badlen, snaplen, ldp->fnm, skip);

  INFORM(" Bum pcap hdr len %d (snaplen %d)\n file %s - EOF encountered after skipping %d bytes\n", badlen, snaplen, ldp->fnm, skip);
  return EOF;
}



int 
check_pkt(bufrec_t *bp)
{

  prec_t p, *pp = &p;
  struct ip iph, *ipp;
  unsigned short iplen;

  pp->brecp = bp;
  pp->buf = buf_start(pp); 
  pp->len = pp->wirelen;

  if (bp->link_data->linkstripper(pp) != ETHERTYPE_IP)
    return 0;

  if (pp->len < sizeof(struct ip))
    return 0;
  
  if (((unsigned int)pp->buf & 0xffffffff) & (sizeof(int) - 1))
    {
      memcpy((void *)&iph, (void *)pp->buf, sizeof(struct ip));
      ipp = &iph;
    }
  else
    {
      ipp = (struct ip *)pp->buf;
    }

  if (ipp->ip_v != IPVERSION)
    return 0;
  
  iplen = ntohs(ipp->ip_len);

  if (iplen != pp->len)
    {
      /* min. length ether frame? */
	if (pp->len > iplen && (IS_ETHER_MIN_FRAME(pp) || IS_FDDI_MIN_FRAME(pp)))
	{
	  return 1;
	}
      else if (pp->wirelen == bp->link_data->snaplen)
	{
	  return 1;
	  
	}
      else if (pp->len < iplen)
	{
	  return 0;
	}
      else
	{
	  return 0;
	}
    }

  return 1;
}

    

  
    
  
  
  



/*
 * end tcpdump_patch.c 
 */

#endif /* TCPDUMP_FED */
