/*  -*- 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 <string.h>
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#ifdef __alpha__
#include <sys/mbuf.h>
#endif
#include <net/route.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#ifdef __alpha__
#include <netinet/ip_var.h>
#endif
#include <netinet/tcp.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>
#include <signal.h>
#include <limits.h>
#include <assert.h>

#include "basic_defs.h"

#include "list.h"

#include "malloc_rec.h"

#include "pkt.h"
#include "seq.h"
#include "flows.h"
#include "http.h"
#include "tcp.h"
#include "service.h"
#include "udp.h"
#include "udp_ns.h"
#include "counters.h"
#include "icmp.h"
#include "print_util.h"
#include "sundry_records.h"
#include "if_stats.h"
#include "procstat.h"
#include "report.h"
#include "output.h"

#include "servers.h"
#include "wread_util.h"
#include "print_util.h"
#include "interesting.h"
#include "content_t.h"
#include "np_file.h"
#include "if_stats.h"

#ifdef SWIG
#include "except.h"
#endif

char *prog = NULL;
char errbuf[100];
long long start_us;
int ncpus;          /* of collecting machine */
int hz;
int pgsz;


void 
wr_error(char *msg)
{
  if (prog)
    fprintf(stderr, "%s: ERROR ", prog);
  else
    fprintf(stderr, "ERROR ");
  //printf("%s: ERROR ", prog);
  if (errno)
    perror(msg);
  else
    fprintf(stderr, "%s\n", msg);
//printf("%d\n", 1/0);
  exit(1);
}

/****************************************************************************/


/*
 * Applicable to file as a whole 
 */

int 
_file_open(struct np_file *file)
{
  char errbuf[100];


  if (!strcmp("-", file->fnm))
    file->file = stdin;
  else if ((file->file = fopen(file->fnm, "r")) == NULL)
    {
      sprintf(errbuf, "can't open %s", file->fnm);
      ERROR(errbuf);
    }

  clearerr(file->file);

  /* get counters and included file header */
  if (fseek(file->file, -sizeof(counters_t), SEEK_END) != 0)
      ERROR("file_open - fseek() counters failed");
  GET_STRUCT(file->file, &file->counters, counters_t);

  if (file->counters.fh.magic != WAN_MAGIC)
    {
      fprintf(stderr, "File %s\n", file->fnm);
      ERROR("file_open - can't locate counters");
    }

  /* Check version */
  if (file->counters.fh.vers_pre != VERS_PRE 
      || file->counters.fh.vers_n != VERS_N)
    {
      char errbuf[256];
      sprintf(errbuf, "Attempt to open Wan Rep file with wrong version reader\nThis is version %c.%u - file is %c.%u", 
	      VERS_PRE, VERS_N,
	      file->counters.fh.vers_pre, file->counters.fh.vers_n);
      wr_error(errbuf);
      /* NOT REACHED */
    }

  /* set log start time global */
  start_us = file->counters.start.tv_sec*US_IN_S_LL;
  /* printf("START_US = %lld\n", start_us); */

  /* set ncpus, hz globals */
  ncpus = file->counters.fh.ncpus;
  hz = file->counters.fh.hz;
  pgsz = file->counters.fh.pgsz;

  /*
   * set file sizes (for backwards compatability fall back on constants if not
   *  saved in log)
   * 
   * TODO - all configuration parameters should be collected together
   *  and saved in log
   * 
   */

  if (file->counters.fh.rep_fsz == 0)
    {
      file->counters.fh.rep_fsz = REPORT_FILE_SZ;
      file->counters.fh.dump_fsz = DUMP_FILE_SZ;
    }


  /* position to read records */
  if (fseek(file->file, 0, SEEK_SET) != 0)
      ERROR("file_open - fseek() records failed");

  file->patches = 0;

#ifdef DUMP_DEBUG
  file->indx = 0;
#endif

  return 0;
}

int 
_file_reset(struct np_file *file)
{
  clearerr(file->file);
  /* position to read records */
  if (fseek(file->file, 0, SEEK_SET) != 0)
      ERROR("file_open - fseek() records failed");

  file->patches = 0;

#ifdef DUMP_DEBUG
  file->indx = 0;
#endif

  return 0;
}


/*
 * Print entire contents - optionally of type X
 */

int _printfile(struct np_file *file, int allocflag, unsigned char type, 
	       int keytype, unsigned int key, int quietflag)

{
  int i;
  int ret = 0;
  int rec_type;
  //unsigned int conn_id;
  tcp_hdrs_t hdrs;
  tcp_conn_t tconn;
  udp_conn_t uconn;
  sk_buff_alloc_fail_rec_t buf_fail;
  period_report_t prep;
  np_rusage_t ru;
  flow_inner_t flow;
  tcp_open_t flow_open;
  ns_fullrec_t ns_rec;
  struct icmp_rec irec;
  wrapper_record_t wrapper;
  procstat_rec_t pr;
  net_device_stats_t nicerrs;
  char rrbuf[NS_MAX_RRBUFSZ];
  int way;
  char intbuf[INTBUF_LEN_MAX + 1];
  int kt = keytype;

  char errbuf[100];

  _rewind(file);

  /* set up transaction chain */
  if (allocflag == TRANS_PREALLOC)
    {
      _http_alloc_trans(&tconn, MAX_NTRANS, PROVIDE_IMGBUFS);
      _tcp_alloc_hdrbuffs(&tconn.hdrs, MAX_TCP_DUMPHDRS_HELD);
      _tcp_alloc_hdrbuffs(&hdrs, MAX_TCP_DUMPHDRS_HELD);
    }

  ns_rec.rrbuf = rrbuf;
  
  /* run through connection records */
  for (;;)
    {
      if ((rec_type = _next_rec(file, type)) == -1)
	break;
      i = file->indx;
      //fprintf(stderr, "reading #%ld\n", i);

      if (kt == KEY_REC)
	{
	  if (i-1 < key)
	    {
	      _advance(file);
	      continue;
	    }
	  else if (i-1 == key)
	    {
	      keytype = 0;
	    }
	  else
	    {
	      break;
	    }
	}

      //printf("%d\n", rec_type);

      switch(rec_type)
	{
	case REC_TCP_TEST_OPEN:
	case REC_TCP_HTTP_OPEN:
	case REC_TCP_FTP_OPEN:
	case REC_TCP_FTP_DATA_OPEN:
	case REC_TCP_RTSP_OPEN:
	case REC_TCP_BGP_OPEN:
	case REC_TCP_PNM_OPEN:
	case REC_TCP_OTHER_OPEN:
	  _read_tcp_open(file, &flow_open);
	  if (!quietflag)
	    if (!keytype 
		|| (keytype == KEY_CONN_ID && flow_open.conn_id == key)
		|| (keytype == KEY_SERV_ADDR && flow_open.flow.dstaddr == key)
		|| (keytype == KEY_CLI_ADDR && flow_open.flow.srcaddr == key))
	      report_tcp_open(&flow_open, flow_open.flow.first_arr_tm, file->indx -1);
	  break;

	case REC_TCP_TEST_HDRS:
	case REC_TCP_HTTP_HDRS:
	case REC_TCP_FTP_HDRS:
	case REC_TCP_FTP_DATA_HDRS:
	case REC_TCP_RTSP_HDRS:
	case REC_TCP_BGP_HDRS:
	case REC_TCP_PNM_HDRS:
	case REC_TCP_OTHER_HDRS:
	  _read_tcp_hdrs(file, &hdrs, allocflag);
	  if (!quietflag)
	    if (!keytype 
		|| (keytype == KEY_CONN_ID && hdrs.conn_id == key)
		|| (keytype == KEY_SERV_ADDR && flow_open.flow.dstaddr == key)
		|| (keytype == KEY_CLI_ADDR && flow_open.flow.srcaddr == key))
	      report_tcp_hdrs(&hdrs, file->indx -1);
	  break;

	case REC_TCP_TEST:
	case REC_TCP_HTTP:
	case REC_TCP_FTP:
	case REC_TCP_FTP_DATA:
	case REC_TCP_RTSP:
	case REC_TCP_BGP:
	case REC_TCP_PNM:
	case REC_TCP_OTHER:
	  _read_tcp_conn(file, &tconn, TRANS_PREALLOC, GET_TRANS);
	  if (!quietflag)
	    if (!keytype 
		|| (keytype == KEY_CONN_ID && tconn.hdrs.conn_id == key)
		|| (keytype == KEY_SERV_ADDR 
		    && tconn.flow_inner.dstaddr == key)
		|| (keytype == KEY_CLI_ADDR 
		    && tconn.flow_inner.srcaddr == key))
	      report_tcp_conn(stdout, &tconn, file->indx -1, 1);
	  break;
	  
	case REC_UDP_NFS:
	case REC_UDP_ICQ:
	case REC_UDP_OTHER:
	  uconn.service_data = NULL;
	  _read_udp_conn(file, &uconn, DATA_PREALLOC, GET_DATA);
	  if (!quietflag && !keytype)
	    report_udp_conn(&uconn, file->indx -1);
	  break;

	case REC_UDP_DNS:
	  uconn.service_data = &ns_rec;
	  _read_udp_conn(file, &uconn, DATA_PREALLOC, GET_DATA);
	  if (!quietflag && !keytype)
	    report_udp_conn(&uconn, file->indx -1);
	  break;

	case REC_ICMP_UNREACH:
	  _read_icmp(file, &irec);
	  if (!quietflag && !keytype)
	    report_icmp(&irec, file->indx -1);
	  break;

	case REC_BUF_ALLOC_FAIL:
	  _read_buf_alloc_fail(file, &buf_fail);
	  if (!quietflag && !keytype)
	    report_buf_alloc_fail(&buf_fail, file->indx -1);
	  break;

	case REC_NIC_FAIL:
	  _read_nic_fail(file, &nicerrs);
	  if (!quietflag && !keytype)
	    report_nic_fail(&nicerrs, file->indx -1);
	  break;

	case  REC_PERIOD_REPORT:
	  /* also attempts to read a following ru record */
	  ret = _read_period_report(file, &prep, &ru);
	  if (!quietflag && !keytype)
	    {
	      report_period_report(&prep, file->indx -2, &file->counters, REC_PERIOD_REPORT);
	      if (!ret)
		report_rusage(&ru, file->indx -1, 0);
	    }
	  
	  break;

	case  REC_WIRE_PERIOD_REPORT:
	  if (type == REC_WIRE_PERIOD_REPORT)
	    {
	      ret = _read_period_report(file, &prep, NULL);
	      report_period_report(&prep, file->indx, &file->counters, REC_WIRE_PERIOD_REPORT);
	    }
	  else
	    {
	      /* just clear it */
	      _advance(file);
	    }
	  break;

	case REC_RUSAGE:
	  _read_rusage(file, &ru);
	  if (!quietflag && !keytype)
	    report_rusage(&ru, file->indx -1, 1);
	  break;

	case REC_INTERESTING:
	  _read_interesting(file, &flow, &way, intbuf);
	  if (!quietflag && !keytype)
	    report_interesting(&flow, way, intbuf, file->indx -1);
	  break;

	case REC_INFORM:
	  _read_inform(file, intbuf);
	  if (!quietflag && !keytype)
	    report_inform(intbuf, file->indx -1);
	  break;

	case REC_OTHER_PROCSTAT:
	  _read_procstats(file, &pr);
	  if (!quietflag && !keytype)
	    report_procstats(&pr, ncpus, hz, pgsz, file->indx -1);
	  break;

	case REC_OTHER_WRAPPER:
	  _read_wrapper(file, &wrapper);
	  if (!quietflag && !keytype)
	    report_wrapper(&wrapper, file->indx -1);
	  break;
	  

	default:
	  sprintf(errbuf, "_printfile(): unknown record type (%d) encountered at record %d file %s", rec_type, file->indx -1, file->fnm);
	  ERROR(errbuf);
	} /* end switch */

      if (ret == -1)
	/* EOF from a _read */
	break;
    }

  if (allocflag == TRANS_PREALLOC)
    {
      /* free transaction chain */
      _http_dealloc_trans(&tconn);
      _tcp_dealloc_hdrbuffs(&tconn.hdrs);
      _tcp_dealloc_hdrbuffs(&hdrs);
    }

  return 0;
}

/*
 * Re-write log file to include only counters and desired record type
 */

#define RECBUF_SZ 16384

int 
_filter_file(struct np_file *filein, int type, char *outfile)
{
  FILE *ofile, *ifile = filein->file;
  int rec_type, len;
  struct rep_rec_hdr rec_hdr;
  unsigned int indx = 0UL;
  char recbuff[RECBUF_SZ];
  

  if ((ofile = fopen(outfile, "w")) == NULL)
    wr_error("_filter_file: fopen()");


  _rewind(filein);

  rec_hdr.magic = REC_HDR_MAGIC;
  
  /* run through connection records */
  for (;;)
    {
      
      if ((rec_type = _next_rec(filein, type)) == -1)
	break;

      len = filein->curr_len;
      
      rec_hdr.indx = indx;
      rec_hdr.type = (unsigned char)(rec_type & 0xff);
      rec_hdr.len = (unsigned int)(len+sizeof(struct rep_rec_hdr));
      
      if (fwrite(&rec_hdr, sizeof(struct rep_rec_hdr), 1, ofile) 
	  != 1)
	{
	  
	  fprintf(stderr, "%s\n", strerror(errno));
	  
	  ERROR("_filter_file: header fwrite()");
	}
      

      GET_MEM(ifile, recbuff, len);
      
      if (fwrite(recbuff, len, 1, ofile) != 1)
	wr_error("_filter_file: record fwrite()");

      indx++;
    }
  
  /*
   *  Now finish off with the counters
   */

  /* step back and check at counters - no need to read them */
   
  if (fseek(ifile, -sizeof(struct rep_rec_hdr), SEEK_CUR) != 0)
    ERROR("_filter_file(): fseek()");

  GET_STRUCT(ifile, &rec_hdr, struct rep_rec_hdr);
  if (feof(ifile))
    ERROR("_filter_file(): didn't find counters");

  filein->counters.nrecords = indx;
  
  rec_hdr.indx = indx;
  rec_hdr.type = REC_COUNTERS;
  rec_hdr.len = sizeof(counters_t);
  
  if (fwrite(&rec_hdr, sizeof(struct rep_rec_hdr), 1, ofile) 
      != 1)
    ERROR("_filter_file: counters header fwrite()");
  
  if (fwrite(&filein->counters, sizeof(counters_t), 1, ofile) 
      != 1)
    wr_error("_filter_file: counters fwrite()");

  if (fclose(ofile) != 0)
    ERROR("_filter_file(): fclose()");

  return 0;
  
}


/*
 * Return offset of current record and advance to next 
 */
long 
_indx_rec(struct np_file *file)
{
  FILE *f = file->file;
  long offset = ftell(f);
  struct rep_rec_hdr rec_hdr;

  GET_STRUCT(f, &rec_hdr, struct rep_rec_hdr);
  
  if (rec_hdr.indx != (file->indx & 0xff))
    {
      char errbuf[250];
      sprintf(errbuf, 
	      "Record delimiter problem: delim %u record %u\n",
	    rec_hdr.indx, file->indx);
      ERROR(errbuf);
    }
  file->indx++;

  if (fseek(f, (long) (rec_hdr.len - sizeof(struct rep_rec_hdr)), 
	    SEEK_CUR) != 0)
    ERROR("next_rec(): fseek()");

  return offset;
}
  


/*
 * Position file at start of next record of specified type
 * - return -1 if EOF else type of record 
 */
int 
_next_rec(struct np_file *file, unsigned char rec_type)
{
  FILE *f = file->file;
  struct rep_rec_hdr rec_hdr;
  long off;
  char errbuf[250];
  

  for (;;)
    {

     GET_STRUCT(f, &rec_hdr, struct rep_rec_hdr);
     if (feof(f))
       {
	 printf("FEOF\n");
	 return -1;
	}

     off = ftell(f);
     if (off < 0)
       ERROR("next_rec(): ftell()");	
     
#ifdef DUMP_DEBUG

      if (rec_hdr.magic != REC_HDR_MAGIC)
	{
	  long end;
	  long start = off-sizeof(struct rep_rec_hdr) + 1; /* fail point + 1 */
	  long pos = start;
	  sprintf(errbuf, 
		  " XXXXX Record delimiter problem: file %s offset %ld following record indx %lu\n",
		 file->fnm, off - sizeof(struct rep_rec_hdr), file->indx-1);

	  if (file->patches++ <= MAX_REC_PATCHES)
	    {
	      fprintf(stderr, "%s\t- attempting recovery\n", errbuf);
	      /* find end - if attempt to go over will get error */
	      if (fseek(f, 0L, SEEK_END) != 0)
		    ERROR("next_rec() recover initial: fseek() end");
	      end = ftell(f) - sizeof(struct rep_rec_hdr);
	      /* back to fail point */
	      if (fseek(f, pos, SEEK_SET) != 0)
		    ERROR("next_rec(): fseek()");	
	      while (pos < end)
		{
		  /* resume 1 byte after failed read */
		  GET_STRUCT(f, &rec_hdr, struct rep_rec_hdr);
		  off = ftell(f);
		  if (off < 0)
		    ERROR("next_rec(): ftell()");
		  if (feof(f))
		    {
		      printf("FEOF\n");
		      return -1;
		    }
		  if (rec_hdr.magic == REC_HDR_MAGIC)
		    {
		      file->indx = rec_hdr.indx;
		      fprintf(stderr, "? recovery at rec. indx %d (+%ld)\n", 
			      file->indx, pos-start);
		      break;
		    }
		  if (fseek(f, ++pos, SEEK_SET) != 0)
		    ERROR("next_rec() recover: fseek()");
		}
	    }
	  else 
	    {
	      fprintf(stderr, "%s too many patches\n", errbuf);
	      ERROR(errbuf);
	    }
	}
      
      if (rec_hdr.indx != file->indx)
	{
	  sprintf(errbuf, 
		  " XXXXX Record index problem: offset %ld record indx %lu type %d expected %lu\n",
		 off - sizeof(struct rep_rec_hdr), rec_hdr.indx, rec_hdr.type,
		  file->indx);
	  ERROR(errbuf);
	}

#else
      
      if (rec_hdr.indx != (file->indx & 0xff))
	{
	  sprintf(errbuf, 
		  " XXXXX Record index problem: offset %ld record indx %x expected %x (%lu) type %d\n",
		 off - sizeof(struct rep_rec_hdr), rec_hdr.indx & 0xff, 
		  file->indx & 0xff, file->indx, rec_hdr.type);
	  ERROR(errbuf);
	}

#endif /* ifdef DUMP_DEBUG */
	  


     file->indx++;
     file->curr_offset = off;
     file->curr_len = rec_hdr.len - sizeof(struct rep_rec_hdr);

      if (rec_hdr.type == REC_COUNTERS)
	{
	  //printf("COUNTERS\n");
	  /* end of records */
	  return -1;
	}
      else if (_is_rectype((int)rec_hdr.type, (int)rec_type))
	{
	  return (int)rec_hdr.type;
	}
      else
	{
	  if (fseek(f, (long) file->curr_len, 
		    SEEK_CUR) != 0)
	    ERROR("next_rec(): fseek()");
	}
    }
  /* NOT REACHED */
  return -1;
}

/*
 * Advance the file to the next record preamble 
 * assumes _next_rec has just been called 
 */
int 
_advance(struct np_file *file)
{
  //if (fseek(file->file, (long) file->curr_len, SEEK_CUR) != 0)
  if (fseek(file->file, (long) file->curr_len + file->curr_offset, SEEK_SET) != 0)
    ERROR("next_rec(): fseek()");
  return 0;
}


int 
_indx(struct np_file *file)
{
  long i;
  unsigned int nrecords = file->counters.nrecords;
  /* get indx space */
  if((file->offsets = (int *)malloc(sizeof(long)*file->counters.nrecords)) == NULL)
    ERROR("_indx(): malloc()");

  /* run through connection records and record offsets */
  for (i = 0; i < nrecords; i++)
    {
      //fprintf(stderr, "indexing #%u\n", i+1);
      file->offsets[i] = _indx_rec(file);
    }
  
  /* position to read records */
  if (fseek(file->file, 0, SEEK_SET) != 0)
      ERROR("_indx() - fseek()  failed");
  
  return 0;
}

int
_rewind(struct np_file *file)
{
  rewind(file->file);
#ifdef DUMP_DEBUG
  file->indx = 0U;
#endif
  return 0;
}

int 
_seek_rec(struct np_file *file, unsigned int recno)
{
  if (fseek(file->file, file->offsets[recno], SEEK_SET) != 0)
    ERROR("_rec_seek: fseek");
#ifdef DUMP_DEBUG
  file->indx = recno;
#endif
  
  return 0;
}

long 
_offset(struct np_file *file)
{
  //long off = ftell(file->file) - sizeof(rep_rec_hdr_t);
  long off = ftell(file->file);
  if (off < 0)
    ERROR("_offset: fseek()");
  else
    return off;

  /* NOT REACHED */
  return 0;
}

int 
_seek(struct np_file *file, long off)
{
  int ret = fseek(file->file, off, SEEK_SET);
  if (ret != 0)
    ERROR("_seek: fseek() fail");
  return ret;
}

/*****************************************************************************/

/* 
 * Record type comparison function 
 */
int
_is_rectype(int type, int wanted)
{
  if (wanted)
    /* not REC_ALL */
    {
      if (type == wanted)
	/* specific type */
	return 1;

      else if (wanted >= REC_TCP_MIN && wanted < REC_TCP_MAX)
	{
	  if (type  == (wanted | OPEN_BIT) || type == (wanted | HDRS_BIT))
	    {
	      return 1;
	    }
	  else if ((wanted & ~(OPEN_BIT | HDRS_BIT)) == REC_TCP_ALL
		   && type > REC_TCP_MIN && type < REC_TCP_MAX)
	    {
	      int wbits = wanted & (OPEN_BIT | HDRS_BIT);
	      int tbits = type & (OPEN_BIT | HDRS_BIT);
	      if ((tbits & wbits) || (tbits == 0))
		return 1;
	      else
		return 0;
	    }
	  else
	    {
	      return 0;
	    }
	}

      else if (wanted == REC_IP)
	if (type > REC_TCP_MIN && type < REC_UDP_MAX)
	  return 1;
	else
	  return 0;
     
      else if (wanted == REC_UDP_ALL)
	if (type > REC_UDP_MIN && type < REC_UDP_MAX)
	  return 1;
	else
	  return 0;
      
      else if (wanted == REC_OTHER_ALL)
	if (type > REC_OTHER_MIN && type < REC_OTHER_MAX)
	  return 1;
	else
	  return 0;
      
      else if (wanted == REC_ICMP_ALL)
	if (type > REC_ICMP_MIN && type < REC_ICMP_MAX)
	  return 1;
	else
	  return 0;
      
      else
	return 0;
    }
  else
    /* accept all */
    return 1;
}

/*****************************************************************************/


/*
 * TCP specific 
 */

int 
_tcp_alloc_hdrbuffs(tcp_hdrs_t *hdrs, int nbufs)
{
 char errbuf[100];

 if ((hdrs->hdrs = (tcp_dumphdr_t *)malloc(sizeof(tcp_dumphdr_t)*nbufs)) == NULL)
   ERROR("_tcp_alloc_hdrbuffs: malloc hdrs buffer");

 return 0;
}

int 
_tcp_dealloc_hdrbuffs(tcp_hdrs_t *hdrs)
{

  free(hdrs->hdrs);

 return 0;
}


/* 
 * Deallocate tcp connection record 
 */
int 
_dealloc_tcp_conn(tcp_conn_t *tconnp)
{
  char errbuf[100];

  /* free any supplementary storage */
  switch (tconnp->flow_inner.serv_type)
    {
    case TCP_SERV_HTTP: _http_dealloc_trans(tconnp); break;
      /* these require no storage beyond the basic tcp connection record */
    case TCP_SERV_OTHER:
    case TCP_SERV_TEST:
    case TCP_SERV_FTP:
    case TCP_SERV_FTP_DATA:
    case TCP_SERV_TELNET:
    case TCP_SERV_SMTP:
    case TCP_SERV_POP3:
    case TCP_SERV_NNTP:
    case TCP_SERV_NETBIOS_SSN:
    case TCP_SERV_RTSP:
    case TCP_SERV_PNM:
      break;

    default:
      sprintf(errbuf, "dealloc_tcp_conn - unknown service type %d", tconnp->flow_inner.serv_type);
      ERROR(errbuf);
      break;
    } /* end switch serv_type */

  if (tconnp->hdrs.hdrs)
    free(tconnp->hdrs.hdrs);

  /* free the connection record */
  free(tconnp);
  
  return 0;
}     

int  
_read_tcp_conn(struct np_file *file, tcp_conn_t *tconnp, int allocflag, int get_trans)
{
  FILE *f = file->file;
  int i;
  int server_seen = 0, client_seen = 0;
  http_trans_t *tp;
  //unsigned int addit_trans_fields;

  char errbuf[256];


#ifdef SWIG
  tconnp->indx = file->indx - 1;
#endif /* SWIG */

  /* get conn_id and absolute open time */
  GET_INT(f, &tconnp->hdrs.conn_id, unsigned int);
  /* get TCP state, ports and addresses */
  GET_STRUCT(f, &tconnp->flow_inner, flow_inner_t);

  client_seen = tconnp->flow_inner.state & TCP_CLIENT_SEEN;
  server_seen = tconnp->flow_inner.state & TCP_SERVER_SEEN;

  /* data on tcp connection */
  if (client_seen)
    GET_STRUCT(f, &tconnp->tcp.client, tcp_simplex_flow_t);
  if (server_seen)
    GET_STRUCT(f, &tconnp->tcp.server, tcp_simplex_flow_t);

  /* get any pkt hdrs info */
  GET_INT(f, &tconnp->hdrs.atm, us_clock_t);
  GET_INT(f, &tconnp->hdrs.nheld, int);

  if (tconnp->hdrs.nheld)
    {
      if (allocflag == TRANS_ALLOC_ON_FLY)
	_tcp_alloc_hdrbuffs(&tconnp->hdrs, tconnp->hdrs.nheld);
      GET_MEM(f, tconnp->hdrs.hdrs, sizeof(tcp_dumphdr_t)*tconnp->hdrs.nheld);
    }
  else if (allocflag == TRANS_ALLOC_ON_FLY)
    {
      tconnp->hdrs.hdrs = NULL;
    }

  switch (tconnp->flow_inner.serv_type)
    {
    case TCP_SERV_HTTP:

      /* data on HTTP transactions */
      
      /* first the status, versions and number of transactions */
      
      GET_STRUCT(f, &tconnp->su.http.meta, http_conn_meta_t);
      
      if (tconnp->su.http.meta.ntrans > MAX_NTRANS)
	{
	  sprintf(errbuf, "%s #%u:\n%s:%s<>%s:%s too many transactions (%hu) \n",
		  file->fnm, file->indx, 
		  get_hname((char *)&tconnp->flow_inner.srcaddr),
		  tcpudp_port_string(ntohs(tconnp->flow_inner.srcport), FLOW_TCP),
		  get_hname((char *)&tconnp->flow_inner.dstaddr),
		  tcpudp_port_string(ntohs(tconnp->flow_inner.dstport), FLOW_TCP),
		  tconnp->su.http.meta.ntrans);
	  ERROR(errbuf);
	}

      /* additional transaction fields present? */
      if (tconnp->su.http.meta.status & HTTP_ADDITIONAL_FIELDS_PRESENT)
	GET_INT(f, &tconnp->su.http.addit_trans_fields, unsigned int);
      else
	tconnp->su.http.addit_trans_fields = 0U;
      
      if (get_trans)
	{
	  if (allocflag == TRANS_ALLOC_ON_FLY)
	    /* allocate transaction chain now */
	    _http_alloc_trans(tconnp, tconnp->su.http.meta.ntrans, NO_IMGBUFS);
	  
	  /* then finally the data for each */
	  for (i = 0, tp = tconnp->su.http.trans; i < tconnp->su.http.meta.ntrans; i++, tp = tp->next)
	    _http_read_trans(tp, file, client_seen, server_seen, tconnp->su.http.addit_trans_fields, allocflag);
	}
      else
	{
	  tconnp->su.http.trans = NULL;
	}
      break;	
      /* end case SERV_HTTP */

    case TCP_SERV_OTHER:
    case TCP_SERV_TEST:
    case TCP_SERV_FTP:
    case TCP_SERV_FTP_DATA:
    case TCP_SERV_TELNET:
    case TCP_SERV_SMTP:
    case TCP_SERV_POP3:
    case TCP_SERV_NNTP:
    case TCP_SERV_NETBIOS_SSN:
    case TCP_SERV_RTSP:
    case TCP_SERV_PNM:
      break;

    default:
      sprintf(errbuf, "read_tconn - unknown service type %d", 
	      tconnp->flow_inner.serv_type);
      ERROR(errbuf);
      break;
    } /* end switch serv_type */
  
  return 0;
}

/*
 * Read TCP connection open record
 */
int 
_read_tcp_open(struct np_file *file, tcp_open_t *flow)
{
  FILE *f = file->file;

  GET_INT(f, &flow->conn_id, unsigned int);
  /* get TCP ports and addresses etc. */
  GET_STRUCT(f, &flow->flow, flow_inner_t);

  return 0;
}


/*
 * Read a block of TCP headers 
 */
int 
_read_tcp_hdrs(struct np_file *file, struct tcp_hdrs *drec, int allocflag)
{
  FILE *f = file->file;

  GET_INT(f, &drec->conn_id, unsigned int);
  GET_INT(f, &drec->atm, us_clock_t);

  drec->nheld = MAX_TCP_DUMPHDRS_HELD;

  //if ((drec->hdrs = (tcp_dumphdr_t *)malloc(sizeof(tcp_dumphdr_t)*MAX_TCP_DUMPHDRS_HELD)) == NULL)
    //ERROR("_read_tcp_hdrs: malloc hdrs buffer");

  if (allocflag == TRANS_ALLOC_ON_FLY)
    _tcp_alloc_hdrbuffs(drec, MAX_TCP_DUMPHDRS_HELD);
  
  GET_MEM(f, drec->hdrs, sizeof(tcp_dumphdr_t)*MAX_TCP_DUMPHDRS_HELD);
  
  return 0;
}

  
  

/****************************************************************************/

/*
 * HTTP specific 
 */

char *
_links_buf_alloc(unsigned int len)
{
  char *cp;

  assert (len != 0);
  if (len == 0)
    {
      fprintf(stderr, "WHOOPS allocating 0 bytes\n");
      exit (1);
    }

  /* allocate single buffer */
  if ((cp = (char *)malloc(len)) == NULL)
	ERROR("img_chars_buf_alloc malloc()");

  return cp;
}

int 
_http_alloc_trans(tcp_conn_t *tconnp, int ntrans, int alloc_imgbuf)
{
  int i;
  http_trans_t **tpp;

  for (i = 0, tpp = &tconnp->su.http.trans; 
       i < ntrans; 
       i++)
    {
      if ((*tpp = (http_trans_t *)malloc(sizeof(http_trans_t))) == NULL)
	ERROR("http_trans malloc()");
      if (alloc_imgbuf == PROVIDE_IMGBUFS)
	{
	  (*tpp)->links.buf 
	    = _links_buf_alloc(LINKS_MAX_BUFS*LINKS_BUFSZ);
	}
      else
	{
	  (*tpp)->links.buf = NULL;
	}
      tpp = &((*tpp)->next);
    }
  *tpp = NULL;

  return 0;
}

/* 
 * Deallocate http transaction chain 
 */
int 
_http_dealloc_trans(tcp_conn_t *tconnp)
{
  http_trans_t *tp = tconnp->su.http.trans;

  while (tp != NULL)
    {
      http_trans_t *tmp = tp;
      if (tp->links.buf != NULL)
	free(tp->links.buf);
      tp = tp->next;
      free(tmp);
    }

  tconnp->su.http.trans = NULL;

  return 0;
}
int 
_http_read_trans(http_trans_t *tp, struct np_file *file, int client_seen, int server_seen, unsigned int addit_fields, int allocflag)
{
  FILE *f = file->file;
  int tmpi;
  
  if (client_seen)
    {
      GET_STRUCT(f, &tp->inner.cinf, http_trans_cinf_t);
      GET_STRUCT(f, &tp->inner.hclient, http_transinf_t);

      if ((tmpi = tp->inner.cinf.reqlen))
	{
	  assert (tmpi <= HTTP_REQSTR_LEN);
	  GET_MEM(f, &tp->req, tmpi);
	}
      tp->req[tmpi] = '\0';
      if ((tmpi = tp->inner.cinf.reflen))
	{
	  assert (tmpi <= HTTP_REQSTR_LEN);
	  GET_MEM(f, &tp->ref, tmpi);
	}
      tp->ref[tmpi] = '\0';
      if ((tmpi = tp->inner.cinf.hostlen))
	{
	  assert (tmpi <= HTTP_REQSTR_LEN);
	  GET_MEM(f, &tp->host, tmpi);
	}
      tp->host[tmpi] = '\0';
      if ((tmpi = tp->inner.cinf.uagentlen))
	{
	  assert (tmpi <= HTTP_REQSTR_LEN);
	  GET_MEM(f, &tp->uagent, tmpi);
	}
      tp->uagent[tmpi] = '\0';
      if ((tmpi = tp->inner.cinf.vialen))
	{
	  assert (tmpi <= HTTP_REQSTR_LEN);
	  GET_MEM(f, &tp->cvia, tmpi);
	}
      tp->cvia[tmpi] = '\0';
      
    }
  if (server_seen)
    {
      GET_STRUCT(f, &tp->inner.sinf, http_trans_sinf_t);
      GET_STRUCT(f, &tp->inner.hserver, http_transinf_t);
/*
 * Comment these out for older vC3 logs 
 */

/*        if (addit_fields) */
/*  	printf("addit %x\n", addit_fields); */
      
      if (addit_fields & AF_FIRST_REP_DATA_TM)
	GET_INT(f, &tp->inner.first_sdata_pkt_us, unsigned int);
      if (addit_fields & AF_REP_HDR_LEN)
	GET_INT(f, &tp->inner.rep_hdr_len, int);

      if ((tmpi = tp->inner.sinf.serverlen))
	{
	  assert (tmpi <= HTTP_REQSTR_LEN);
	  GET_MEM(f, &tp->server, tmpi);
	}
      tp->server[tmpi] = '\0';
      if ((tmpi = tp->inner.sinf.vialen))
	{
	  assert (tmpi <= HTTP_REQSTR_LEN);
	  GET_MEM(f, &tp->svia, tmpi);
	}
      tp->svia[tmpi] = '\0';

      GET_INT(f, &tp->links.totchars, unsigned int);

      /* XXX TMP */
      if(tp->links.totchars > LINKS_BUFSZ*LINKS_MAX_BUFS) 
	printf("XXXXXXXXXX links chars too long %d\n", tp->links.totchars);
      assert (tp->links.totchars <= LINKS_BUFSZ*LINKS_MAX_BUFS);

      if (tp->links.totchars)
	{
	  /* some URLs to get here - allocate space if not pre-allocated */
	  if (allocflag == TRANS_ALLOC_ON_FLY)
	    {
	      if (tp->links.totchars > LINKS_MAX_BUFS*LINKS_BUFSZ)
		{
		  fprintf (stderr, "WHOOPS - infeasable links.totchars\n");
		  tp->links.totchars =  LINKS_MAX_BUFS*LINKS_BUFSZ;
		}
	      tp->links.buf = 
		_links_buf_alloc(tp->links.totchars);
	    }
	  GET_MEM(f, tp->links.buf, tp->links.totchars);
	}	
    }	

  return 0;
} 

/*
 * Get the transaction chain for an HTTP connection
 */
int 
_http_get_transchain(struct np_file *file, tcp_conn_t *tconnp)
{
  int i;
  int client_seen = tconnp->flow_inner.state & TCP_CLIENT_SEEN;
  int server_seen = tconnp->flow_inner.state & TCP_SERVER_SEEN;
  http_trans_t *tp;
  //unsigned int addit_trans_fields;
  
  /* allocate transaction chain */
  _http_alloc_trans(tconnp, tconnp->su.http.meta.ntrans, NO_IMGBUFS);
  /* additional transaction fields present? */
  if (tconnp->su.http.meta.status & HTTP_ADDITIONAL_FIELDS_PRESENT)
    GET_INT(file->file, &tconnp->su.http.addit_trans_fields, unsigned int);

  for (i = 0, tp = tconnp->su.http.trans; i < tconnp->su.http.meta.ntrans; i++, tp = tp->next)
    _http_read_trans(tp, file, client_seen, server_seen, tconnp->su.http.addit_trans_fields, TRANS_ALLOC_ON_FLY);

  return 0;
}

/*
 * Count the number of link/image URLs contained in a transaction record 
 */

/*
 * NB - since introduction of record types and base URL scoping this does 
 * not work
 */

int 
_http_get_nurls(char *chars, int len)
{
  char *end = chars + len;
  int count = 0;

  while (chars < end)
    {
      if (*chars == '\0')
	{
	  /* end of string */
	  count++;
	  chars++;
	  /* timestamp? */
	  if (*chars == '\0')
	    chars += (2 + sizeof(uint));
	}
      else 
	{
	  chars++;
	}
    }

  return count;
}

/*
 * Count the number of distinct link/image URLs contained in a transaction 
 * record 
 */

/*
 * NB - since introduction of record types and base URL scoping this does 
 * not work
 */

#define N_HT_SLOTS 111

int 
_http_get_ndist_urls(char *chars, int buflen)
{
  struct lrec 
    {
      struct lrec *next;
      char *url;
      int first_arrtm;
      int n;
    };
  struct lrec hlist[N_HT_SLOTS];
  unsigned int hval;

  char *end = chars + buflen;
  char *url;
  int urlen;
  int i,j;
  int ts;

  int count = 0;

  memset(&hlist, 0, N_HT_SLOTS*sizeof(struct lrec));

  while (chars < end)
    {
      int found = 0;
      struct lrec *lp;
      url = chars;
      urlen = strlen(url);
      chars += urlen + 1;
      hval = 0;

      if (*chars == '\0')
	{
	  /* time stamp */
	  //printf("+ts ");
	  ts = *((int *)++chars);
	  chars += 2 + sizeof(int);
	}

      j = urlen/sizeof(int);
      for (i = 0; i < j ; i++)
	hval += ((int *)url)[i];

      j = urlen % sizeof(int);
      for (i = urlen - j; i < urlen; i++)
	hval += (int)url[i];

      hval = hval % N_HT_SLOTS;

      //printf("%s %u", url, hval);

      for (lp = &hlist[hval]; lp->next; lp = lp->next)
	{
	  if (lp->url && !strcmp(url, lp->url))
	    {
	      found++;
	      break;
	    }
	}

      if (found)
	{
	  lp->n++;
	  //printf(" duplicate\n");
	}
      else
	{
	  //printf("\n");
	  count++;
	  lp->url = url;
	  lp->n++;
	  lp->next = (struct lrec *)calloc(1, sizeof(*lp));
	  lp->first_arrtm = ts;
	}
    }

  return count;
}


/*****************************************************************************/

/*
 * DNS specific 
 */

int 
_read_ns_rec(struct np_file *file, ns_fullrec_t *np, int allocflag)
{
  FILE *f = file->file;
  
  GET_STRUCT(f, &np->ns_rec, ns_rec_t);
  
  if (np->ns_rec.state & NS_RRS_FOLLOW)
    /* got some resource records */
    {
      if (allocflag == DATA_ALLOC_ON_FLY)
	{
	  if ((np->rrbuf = (char *)malloc(NS_MAX_RRBUFSZ)) == NULL)
	    {
	      sprintf(errbuf, "_read_udp_conn() - ns_rr buffer malloc fail");
	      ERROR(errbuf);
	    }
	}
      assert (np->ns_rec.buflen <= NS_MAX_RRBUFSZ);
      GET_MEM(f, np->rrbuf, np->ns_rec.buflen);
    }
  else
    {
      if(allocflag == DATA_ALLOC_ON_FLY) 
	np->rrbuf = NULL;
    }
  
  return 0;
}

int 
_dealloc_ns_rec(ns_fullrec_t *np)
{
  if (np)
    {
      if (np->rrbuf)
	free(np->rrbuf);
      
      free(np);
    }

  return 0;
}

/*****************************************************************************/

/*
 * UDP flow specific
 */


/* 
 * Deallocate udp connection record 
 */
int 
_dealloc_udp_conn(udp_conn_t *uconnp)
{
  char errbuf[100];

  /* free any supplementary storage */
  switch (uconnp->flow_inner.serv_type)
    {
      /* these require no storage beyond the basic udp connection record */
    case UDP_SERV_OTHER:
    case UDP_SERV_NFS:
      assert(uconnp->service_data == NULL);
      break;
    case UDP_SERV_DNS:
      _dealloc_ns_rec((ns_fullrec_t *)uconnp->service_data);
      break;
    default:
      sprintf(errbuf, "_dealloc_udp_conn - unknown service type %d", uconnp->flow_inner.serv_type);
      ERROR(errbuf);
      break;
    } /* end switch serv_type */

  /* free the connection record */
  free(uconnp);
  
  return 0;
}

int
_read_udp_conn(struct np_file *file, udp_conn_t *uconnp, int allocflag, int get_data)
{

  FILE *f = file->file;
  int server_seen = 0, client_seen = 0;

  struct ns_fullrec *np;

  char errbuf[256];

#ifdef SWIG
  uconnp->indx = file->indx - 1;
#else
  uconnp->indx = 0;
#endif /* SWIG */

  /* get UDP state, ports and addresses */
  GET_STRUCT(f, &uconnp->flow_inner, flow_inner_t);

  client_seen = uconnp->flow_inner.state & UDP_CLIENT_SEEN;
  server_seen = uconnp->flow_inner.state & UDP_SERVER_SEEN;

  /* data on udp connection */
  if (client_seen)
    GET_STRUCT(f, &uconnp->udp.client, udp_simplex_flow_t);
  if (server_seen)
    GET_STRUCT(f, &uconnp->udp.server, udp_simplex_flow_t);

  switch (uconnp->flow_inner.serv_type)
    {
    case UDP_SERV_NFS:
    case UDP_SERV_ICQ:
    case UDP_SERV_OTHER:
      break;
    case UDP_SERV_DNS:
      if (get_data)
	{  
	  if (allocflag == DATA_ALLOC_ON_FLY)
	    {
	      /* Need to allocate space */
	      if ((np = malloc(sizeof(ns_fullrec_t))) == NULL)
		{
		  sprintf(errbuf, 
			  "_read_udp_conn() - ns_fullrec_t malloc fail");
		  ERROR(errbuf);
		}
	      np->rrbuf = NULL;
	      uconnp->service_data = np;
	    }
	  else
	    {
	      np = (struct ns_fullrec *)uconnp->service_data;
	    }
	  _read_ns_rec(file, np, allocflag);
	}
      break;
      
    default:
      sprintf(errbuf, "read_uconn - unknown service type %d file %s record %u", uconnp->flow_inner.serv_type, file->fnm, file->indx -1);
      ERROR(errbuf);
      break;
    } /* end switch serv_type */
  
  return 0;
}

/*****************************************************************************/

/*
 * ICMP specific
 */

int 
_read_icmp(struct np_file *file, struct icmp_rec *ipp)
{
  FILE *f = file->file;

  GET_STRUCT(f, ipp, struct icmp_rec);

  return 0;
}

  

/*****************************************************************************/

/*
 * Skb_buff alloc fail specific
 */

int  
_read_buf_alloc_fail(struct np_file *file, sk_buff_alloc_fail_rec_t *fail_rec)
{
  FILE *f = file->file;

#if 0
#ifdef SWIG
  fail_rec->indx = file->indx - 1;
#endif /* SWIG */
#endif

  GET_STRUCT(f, fail_rec, sk_buff_alloc_fail_rec_t);

  return 0;
}

  

/*****************************************************************************/

/*
 * Interface card fail/drop specific
 */

int  
_read_nic_fail(struct np_file *file, net_device_stats_t *fail_rec)
{
  FILE *f = file->file;

#if 0
#ifdef SWIG
  fail_rec->indx = file->indx - 1;
#endif /* SWIG */
#endif

  GET_STRUCT(f, fail_rec, net_device_stats_t);

  return 0;
}

/*****************************************************************************/

/*
 * Periodic report specific 
 */

int 
_read_period_report(struct np_file *file, period_report_t *prep, 
		    np_rusage_t *rup)
{
  FILE *f = file->file;
  int rec_type;

#if 0
#ifdef SWIG
  prep->indx = file->indx - 1;
#endif /* SWIG */
#endif

  GET_STRUCT(f, prep, period_report_t);

  if (rup)
    {
      /*
       * Should be followed by a rusage report - get that too
       */

      rec_type = _next_rec(file, REC_ALL);
      
      if (rec_type == REC_RUSAGE)
	{
	  /* got it */
	  _read_rusage(file, rup);
	  return 0;
	}
      else if (rec_type == -1)
	{
	  /* EOF */
	  return -1;
	}
      else
	{
	  /* wasn't one - rewind over rec hdr */
	  if (fseek(file->file, - sizeof(struct rep_rec_hdr), SEEK_CUR) != 0)
	    ERROR("_read_period_reort(): fseek()");
	  file->indx--;
	  file->curr_offset -= sizeof(struct rep_rec_hdr);
	  return 1;
	}
    }
  else
    {
      return 0;
    }
  
  /* NOT REACHED */;
}

/*****************************************************************************/

/*
 * Rusage report specific 
 */

int 
_read_rusage(struct np_file *file, np_rusage_t *ru)
{
  FILE *f = file->file;

  GET_STRUCT(f, ru, np_rusage_t);

  return 0;
}

/*****************************************************************************/

/*
 * `Interesting' specific 
 */

int 
_read_interesting(struct np_file *file, flow_inner_t *flowp, int *wayp, char *s)
{
  FILE *f = file->file;
  GET_STRUCT(f, flowp, flow_inner_t);
  GET_INT(f, wayp, int);
  GET_STRING(f, s);

  return 0;
}

int 
_read_inform(struct np_file *file, char *s)
{
  GET_STRING(file->file, s);

  return 0;
}

/*****************************************************************************/

/*
 * 'Wrapper' record type 
 */

int 
_read_wrapper(struct np_file *file, wrapper_record_t *wrapper)
{
  FILE *f = file->file;
  GET_STRUCT(f, wrapper, wrapper_record_t);

  return 0;
}

/*****************************************************************************/

/*
 * Procstat record type 
 */

int 
_read_procstats(struct np_file *file, procstat_rec_t *prp)
{
  FILE *f = file->file;
  GET_STRUCT(f, prp, procstat_rec_t);

  return 0;
}
  

/*****************************************************************************/

/*
 * Sundry stuff 
 */


/*
 * Copy record at offset off to file fd renumbering as n - return new offset
 */

int _copy_rec(struct np_file *file, long int off, int fd, int n)
{
  struct rep_rec_hdr hdr;
  FILE *thisf = file->file;
  char *recbuf;
  int len;
  long int new_off;
  
  
  _seek(file, off-sizeof(struct rep_rec_hdr));
  if (fread(&hdr, sizeof(struct rep_rec_hdr), 1, thisf) != 1)
    {
      if (feof(thisf))
	{
	  fprintf(stderr, "copy_rec: fread() hdr - %s EOF reached", file->fnm);
	  exit (1);
	}
      else if (ferror(thisf))
	{
	  fprintf(stderr, "copy_rec: fread() hdr - %s EOF error", file->fnm);
	  exit (1);
	}
      else
	{
	  fprintf(stderr, "copy_rec: fread() hdr - %s error", file->fnm);
	  exit (1);
	}
    } 
  
  hdr.indx = n;
  len = hdr.len - sizeof(struct rep_rec_hdr);

  if (write(fd, &hdr, sizeof(struct rep_rec_hdr)) != sizeof(struct rep_rec_hdr))
    {
      perror("copy_rec: write hdr");
      exit (1);
    }

  if ((new_off = lseek(fd, 0, SEEK_CUR)) == -1)
    {
      fprintf(stderr, "copy_rec: lseek() error");
      exit (1);
    }

  if ((recbuf = (char *)malloc(len)) == NULL)
    {
      fprintf(stderr, "copy_rec: malloc error");
      exit (1);
    }

  if (fread(recbuf, len, 1, thisf) != 1)
    {
      if (feof(thisf))
	{
	  fprintf(stderr, "copy_rec: fread() rec - %s EOF reached", file->fnm);
	  exit (1);
	}
      else if (ferror(thisf))
	{
	  fprintf(stderr, "copy_rec: fread() rec - %s EOF error", file->fnm);
	  exit (1);
	}
      else
	{
	  fprintf(stderr, "copy_rec: fread() rec - %s error", file->fnm);
	  exit (1);
	}
    }

  if (write(fd, recbuf, len) != len)
    {
      perror("copy_rec: write rec");
      exit (1);
    }

  return (int) new_off;
}

/*
 * Adapt counters and write to new rep file
 */

void _write_pseudocounters(int fd, int reci, counters_t *counters)
{
  rep_rec_hdr_t rhdr;
  fhdr_t hdr, *fhdr = &counters->fh;

  memset(&hdr, '\0', sizeof(fhdr_t));
  
  hdr.magic = fhdr->magic;
  hdr.vers_pre = fhdr->vers_pre;
  hdr.vers_n = fhdr->vers_n;
  
  memset(counters, '\0', sizeof(counters_t));
  memcpy(fhdr, &hdr, sizeof(fhdr_t));

  rhdr.magic = REC_HDR_MAGIC;
  rhdr.type = REC_COUNTERS;
  rhdr.indx = reci;
  rhdr.len = sizeof(counters_t) + sizeof(rep_rec_hdr_t);
  

  if (write(fd, &rhdr, sizeof(rep_rec_hdr_t)) != sizeof(rep_rec_hdr_t))
    {
      perror(" _write_pseudocounters(): write counters");
      exit (1);
    }
  

  if (write(fd, counters, sizeof(counters_t)) != sizeof(counters_t))
    {
      perror(" _write_pseudocounters(): write counters");
      exit (1);
    }

  return;
}

  
  

/* Parse CL record type arg */

unsigned char 
_parse_type(char *ts)
{
  unsigned char wanted = REC_ALL;
  char *endp;

  long code;

  while (*ts == ' ')
    ts++;

  code = strtol(ts, &endp, 0);
  if (endp != ts)
    {
      if (code > REC_MAX)
	{
	  fprintf(stderr, "Record type specified (%ld) > highest allowed (%d)\n", code, REC_MAX);
	  exit(1);
	}
      else
	{
	  return (unsigned char)code;
	}
    }

  if (strlen(ts) == 1)
    {
      if (*ts == 'i')
	wanted = REC_IP;
      if (*ts == 't')
	wanted = REC_TCP_ALL;
      else if (*ts == 'u')
	wanted = REC_UDP_ALL;
      else if (*ts == 'o')
	wanted = REC_OTHER_ALL;
      else if (*ts == 'I')
	wanted = REC_INTERESTING;
      else if (*ts == 'P')
	wanted = REC_PERIOD_REPORT;
      else if (*ts == 'R')
	wanted = REC_RUSAGE;
    }

  if (wanted)
    ts++;

  while (*ts == ' ')
    ts++;	

  if (!strlen(ts))
    {
      return wanted;
    }
  else
    {
      if (!strcmp(ts, "all"))
	{
	  return REC_ALL;
	}
      else if (!strcmp(ts, "tcp"))
	{
	  return REC_TCP_ALL;
	}
      else if (!strcmp(ts, "udp"))
	{
	  return REC_UDP_ALL;
	}
      else if (!strcmp(ts, "ip"))
	{
	  return REC_IP;
	}
      else if (!strcmp(ts, "other"))
	{
	  return REC_OTHER_ALL;
	}
      /* specific TCP */
      else if (!strcmp(ts, "test"))
	{
	  return REC_TCP_TEST;
	}
      else if (!strcmp(ts, "http"))
	{
	  return REC_TCP_HTTP;
	}
      else if (!strcmp(ts, "ftp"))
	{
	  return REC_TCP_FTP;
	}
      else if (!strcmp(ts, "ftp-data"))
	{
	  return REC_TCP_FTP_DATA;
	}
      else if (!strcmp(ts, "rtsp"))
	{
	  return REC_TCP_RTSP;
	}
      else if (!strcmp(ts, "pnm"))
	{
	  return REC_TCP_PNM;
	}
	  /* specific UDP */
      else if (!strcmp(ts, "nfs"))
	{
	  return REC_UDP_NFS;
	}
      else if (!strcmp(ts, "dns"))
	{
	  return REC_UDP_DNS;
	}
	  /* specific ICMP */
      else if (!strcmp(ts, "icmp"))
	{
	  return REC_ICMP_ALL;
	}
	  /* specific OTHER */
      else if (!strcmp(ts, "fails"))
	{
	  return REC_BUF_ALLOC_FAIL;
	}
      else if (!strcmp(ts, "nic"))
	{
	  return REC_NIC_FAIL;
	}
      else if (!strcmp(ts, "rep"))
	{
	  return REC_PERIOD_REPORT;
	}
      else if (!strcmp(ts, "rus"))
	{
	  return REC_RUSAGE;
	}
      else if (!strcmp(ts, "PW"))
	{
	  return REC_WIRE_PERIOD_REPORT;
	}
      else if (!strcmp(ts, "PP"))
	{
	  return REC_OTHER_PROCSTAT;
	}
      else if (!strcmp(ts, "tmp"))
	{
	  return REC_OTHER_WRAPPER;
	}
      else if (!strcmp(ts, "bgp"))
	{
	  return REC_TCP_BGP;
	}
      else if (!strcmp(ts, "inform"))
	{
	  return REC_INFORM;
	}
	  /* other sub-type of TCP/UDP/OTHER */
      else if (!strcmp(ts, "other"))
	{
	  if (wanted == REC_TCP_ALL)
	    return REC_TCP_OTHER;
	  else if (wanted == REC_UDP_ALL)
	    return REC_UDP_OTHER;
	  else if (wanted == REC_OTHER_ALL)
	    return REC_OTHER_OTHER;
	}
      else
	{
	  return  REC_ALL;
	}
    }
  return wanted;
}

/*****************************************************************************/

int 
_http_print_like_squidlog(http_trans_t *trans, tcp_conn_t *tconn, int code)
{
  us_clock_t trans_start = trans->inner.cinf.reqstart_us + tconn->flow_inner.first_arr_tm;
  us_clock_t trans_end = trans->inner.sinf.repend_us + tconn->flow_inner.first_arr_tm;
  unsigned int rep_time_us;
  struct timeval transtime;
  unsigned int len;
  int rel_url = 0;

  /* Check times are ok - will not have got here if trans_start not valid */
  assert(trans_start != 0ULL);
  if (trans_end != 0)
    rep_time_us = trans_end - trans_start;
  else
    rep_time_us = 999999999;

  /* 
   * Check code
   * -1 = valid
   *  0 = not valid - shouldn't have got here
   * >1 = inferred valid
   *     1 - 4 = known body length
   *     > 4 = assume nil (or invalid) body length XXX FIX THIS
   */

  assert (code != 0);
#if 0
  if (code < 5)
    len = trans->inner.hserver.recd_len;
  else
    len = 0UL;
#endif

  len = trans->inner.hserver.recd_len;

  /* check URL */
  if (strncmp(trans->req, "http://", strlen("http://")))
    {
      rel_url++;
      printf("# Relative URL\n");
    }

  transtime.tv_sec = trans_start/US_IN_S;
  transtime.tv_usec = trans_start % US_IN_S;

  printf("%lu.%.3lu ", transtime.tv_sec, transtime.tv_usec/1000);
  printf("%7lu ", rep_time_us/1000);

#if 0
  printf("%s (%s/%s) ", get_hname(&tconn->flow_inner.srcaddr), 
	 tcpport_string(ntohs(tconn->flow_inner.srcport)),
	 tcpport_string(ntohs(tconn->flow_inner.dstport)));
#endif

  printf("%s ", get_hname((char *)&tconn->flow_inner.srcaddr));
  printf("TCP_MISS/%u ", trans->inner.sinf.status_code);
  printf("%lu ", len);
  printf("%s ", method_string(trans->inner.cinf.method));
	 
  if (rel_url)
    printf("http://%s", get_hname((char *)&tconn->flow_inner.dstaddr));

    printf("%s ", trans->req);

  printf("- ");
  printf("DIRECT/");
  printf("%s ", get_hname((char *)&tconn->flow_inner.dstaddr));
  
  if (trans->inner.hserver.content_type == CT_UNKNOWN)
    printf("- ");
  else
    printf("%s ", content_type_string(trans->inner.hserver.content_type));

  printf("-\n");

  return 0;
}


/*****************************************************************************/

  

/*
 * End wread_util.c 
 */
