/*
    Program:     tcpfix.c
    Author:      Rob Beverly <rbeverly \at\ mit \d0t\ edu>
    Date:        March 11, 2003
    Updated:
    Description: Attempts to fix a corrupt TCP dump file.  

    Strategy is to search for and count the first SCANDEPTH 4-byte words 
    for values less than MAXSNAPLEN.  Assumption is that since each
    packet is prefaced with a pcap_pkthdr and the pkthdr caplen is fixed,
    this will let us find a packet boundary.  Once the most common caplen
    is found, seek to the first occurance of caplen and then rewind
    caplen + struct timeval (12bytes) back.  This should be the beginning
    of a real (good) packet (hopefully).  Write out a real pcap_file_header
    (need to know the right DLT linktype, specify with -d on command 
    line) and then append packets starting from the first good packet.
    
    This program won't work for a tcpdump file that's corrupted on some
    weird byte boundary or other craziness - only for files whose 
    beginning was somehow truncated or trashed.   
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pcap.h>

#define VERSION "0.1"
#define SCANDEPTH 500
#define MAXSNAPLEN 1500
#define TCPDUMP_MAGIC 0xa1b2c3d4

int sf_write_header(FILE *, int, int, int);

void usage(char *prog) {
   printf("usage: %s -i<input> -d<dlttype>\n", prog);
   printf("       dlt:  1 = ethernet (default)\n");
   printf("            12 = raw\n"); 
   exit(-1);
}

int main(int argc, char **argv) {
  FILE *fp, *fpout;
  char *input = NULL;
  char temp[256];
  int count[MAXSNAPLEN];
  int snaplen = 0, c, ch, i;
  int max = 0; 
  int dlt = 1;
 
  printf("%s v%s - attempt tcpdump fix.\n", argv[0], VERSION);

  /* Parse the command-line. */
  while ((ch = getopt(argc, argv, "hi:d:")) != EOF) {
      switch ((char) ch) {
      case 'h':
          usage(argv[0]);
          break;
      case 'i':
          input = optarg;
          break;
      case 'd':
          dlt = atoi(optarg);
          break;
      }  
  }

  if (!input) usage(argv[0]);
 
  if ((fp = fopen(input, "r")) == NULL) {
    fprintf(stderr, "Error opening: %s\n", input);  
    exit(-1);
  } 
  printf("Opening: %s.\n", input);

  fread(&c, sizeof(int), 1, fp);
  if (c == TCPDUMP_MAGIC) {
    fprintf(stderr, "%s does not appear corrupt!\n", input);
    fprintf(stderr, "Bye.\n");
    exit(-1);
  } 

  /* Corrupt file - start from the top. */
  printf("Trace looks corrupt, begin repair.\n");
  rewind(fp);
  printf("Examining trace...\n");

  /* Find most common integer > 0 && < MAXSNAPLEN as our caplen */
  bzero(&count, sizeof(int) * MAXSNAPLEN);
  for (i=0;i<SCANDEPTH;i++) {
    fread(&c, sizeof(int), 1, fp);
    if (c < MAXSNAPLEN && c > 0) {
       count[c]++;
    }
  }
  for (i=0;i<MAXSNAPLEN;i++) {
    if (count[i] > max) {
      max = count[i];
      snaplen = i;
    }
  }

  printf("Best guess caplen: %d (%d occurances).\n", snaplen, max);
  printf("Rewinding trace.\n");

  rewind(fp);
  i = 0;
  while (c != snaplen) {
    fread(&c, sizeof(int), 1, fp);
    i++;
  }

  printf("First occurence of caplen %d seen %d bytes into trace.\n", snaplen, i*4);
  i = ((i - 3)*4);
  printf("Seek to first pcap_pkthdr at byte %d.\n", i);
  fseek(fp, i, SEEK_SET);

  sprintf(temp, "%s.fixed", input);
  fpout = fopen(temp, "w");

  printf("Writing sane header to: %s.\n", temp);
  
  sf_write_header(fpout, dlt, 0, snaplen);

  printf("Writing packets...\n");
  while (!feof(fp)) {
     fread(&c, sizeof(int), 1, fp);
     fwrite(&c, sizeof(int), 1, fpout); 
  }

  printf("Done.  Hope it works out!\n");
  fclose(fpout);
  fclose(fp);
  exit(0);
}


int sf_write_header(FILE *fp, int linktype, int thiszone, int snaplen)
{
        struct pcap_file_header hdr;
 
        hdr.magic = TCPDUMP_MAGIC;
        hdr.version_major = PCAP_VERSION_MAJOR;
        hdr.version_minor = PCAP_VERSION_MINOR;
  
        hdr.thiszone = thiszone;
        hdr.snaplen = snaplen;
        hdr.sigfigs = 0;
        hdr.linktype = linktype;
 
        if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1)
                return (-1);

        return (0);
}
