/*  -*- 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 <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/socket.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>
#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 <net/ethernet.h>

#include "linux_tweaks.h"

#endif

#include <assert.h>

#include "list.h"
#include "pkt.h"
#include "interface.h"
#include "flows.h"
#include "service.h"
#include "http.h"
#include "tcp.h"
#include "udp.h"
#include "udp_ns.h"
#include "seq.h"
#include "report.h"

#ifdef PRINT_OUT
#include "print_util.h"
#endif

#include "output.h"
#include "pool.h"

#include "writer.h"
#include "print_util.h"
#include "content_t.h"
#include "interesting.h"
#include "sundry_records.h"


void tcp_http_open(prec_t *pp, struct tcp_conn *tconnp, int way, unsigned char flags)
{
  if (flags & TH_SYN)
    {
      /* Ensure client is opening connection */
      if (TCP_BOTH_SEEN)
	{
	  if (way == SERVER)
	    {
	      if (!(flags & TH_ACK))
		{
		  /* server is opening connection */
		  //http_error(tconnp, pp, HTTP_SERV_ERR_SERVER_OP, way, NULL);
		  new_http_trans(tconnp, way);
		  HTTP_NTRANS_DUM++;
		  HTTP_REP_ERR = HTTP_SERV_ERR_SERVER_OP;
		  HTTP_REP_TRANS_STATUS |= TRANS_ERR;
		  HTTP_REP_TRANS_STATUS |= TRANS_DUMMY_ERR;
		  HTTP_REP_CONTENT_T = CT_POST_ERR;
		  HTTP_FIRSTREP_SEEN = TCP_SSOLID_TM;
		  HTTP_LASTREP_SEEN = TCP_SSOLID_TM;
		  HTTP_LASTDATA_SEEN = TCP_SSOLID_TM;
		}
	      
	    }
	  else
	    {
	      if (flags & TH_ACK)
		{
		  /* server has opened connection */
		  //http_error(tconnp, pp, HTTP_CLI_SERVER_ACTIVE, way, NULL);
		  new_http_trans(tconnp, way);
		  HTTP_NTRANS_DUM++;
		  HTTP_REQ_ERR = HTTP_CLI_SERVER_ACTIVE;
		  HTTP_REQ_TRANS_STATUS |= TRANS_ERR;
		  HTTP_REQ_TRANS_STATUS |= TRANS_DUMMY_ERR;
		  HTTP_REQ_CONTENT_T = CT_POST_ERR;
		  HTTP_FIRSTREQ_SEEN = TCP_SSOLID_TM;
		  HTTP_LASTREQ_SEEN = TCP_SSOLID_TM;
		}
	    }
	}
    }
  else
    {
      /* 
       * first pkt not SYN - can't get synchronisation 
       * - get dummy trans
       * NB - don't know which is first - get timing info right 
       */
      new_http_trans(tconnp, way);	/* set up dummy to collect it */
      HTTP_NTRANS_DUM++;
      if (way == SERVER)
	{
	  HTTP_REP_TRANS_STATUS |= TRANS_DUMMY_UNSYNCH;
	  HTTP_REP_CONTENT_T = CT_UNSYNCH;
	  HTTP_FIRSTREP_SEEN = TCP_SSOLID_TM;
	  HTTP_LASTREP_SEEN = TCP_SSOLID_TM;
	}
      else
	{
	  HTTP_REQ_TRANS_STATUS |= TRANS_DUMMY_UNSYNCH;
	  HTTP_REQ_CONTENT_T = CT_UNSYNCH;
#if 0
	  if (tconnp->su.http.reqtrans->inner.s.ti.status & TRANS_VAL)
	    { 
	      /* already seen server */
	      if (tconnp->su.http.reqtrans->inner.sinf.repstart_us 
		   <  TCP_CSOLID_TM)
		{
		  HTTP_FIRSTREQ_SEEN = tconnp->su.http.reqtrans->inner.sinf.repstart_us;
		  HTTP_REQ_TRANS_STATUS |= TRANS_TIMING_PATCHED;
		}
	      else
		{
		HTTP_FIRSTREQ_SEEN = TCP_CSOLID_TM;
	      }
	    }
	  else
	    {
	      HTTP_FIRSTREQ_SEEN = TCP_CSOLID_TM;
	    }
#endif
	  HTTP_FIRSTREQ_SEEN = TCP_CSOLID_TM;
	  HTTP_LASTREQ_SEEN = HTTP_FIRSTREQ_SEEN;
	}
    }
  return;
}

void tcp_http_close(prec_t *pp, struct tcp_conn *tconnp, int way, unsigned char flags)
{
  if (way == CLIENT)
    {
      ;				/* nothing */
    }
  else
    {
      /*
       * Object completion is denoted by (in order of percedence):
       * a) Mandatory no body
       * b) Chunked encoding
       * c) Bytes received == notified content length
       * d) Bytes received == length implied in "multipart/byteranges" request
       * e) Server close  (non-persistent connections)

       * We need to recognise and deal with case e) here.
       */

      if ((IS_NON_PERSISTENT_CONN || HTTP_PERSISTENCE_UNKNOWN)
	  && tconnp->su.http.reptrans
	  && HTTP_REP_BODY_LEN == HTTP_BODY_LEN_UNKNOWN)
	{
	  trans_complete(tconnp, SERVER);
	}
    }

  return;
}

#if 0
void close_imgchars(http_trans_t *tp)
{
  il_imgchars_buf_t *curr = tp->il_imagechars.current;
  int nchars = tp->il_imagechars.nchars;
  
  /* finished part-way through a string? */
  if (curr->buf[nchars -1] != '\0')
    {
      curr->buf[nchars] = '\0';
      nchars++;
      /* XXX TODO record this */
    }
 
  /* update current buffer */
  curr->nchars = nchars;
  
  /* update total */
  tp->il_imagechars.totchars += nchars;

  return;
}

void close_link_urls(http_trans_t *tp)
{
  il_imgchars_buf_t *curr = tp->link_urls.current;
  int nchars = tp->link_urls.nchars;
  
  /* finished part-way through a string? */
  if (curr->buf[nchars -1] != '\0')
    {
      curr->buf[nchars] = '\0';
      nchars++;
      /* XXX TODO record this */
    }
 
  /* update current buffer */
  curr->nchars = nchars;
  
  /* update total */
  tp->link_urls.totchars += nchars;

  return;
}
#endif

void close_link_urls(struct links_chars *lp)
{
  links_buf_t *curr = lp->current;
  int nchars = lp->nchars;
  
  /* finished part-way through a string? */
  if (curr->buf[nchars -1] != '\0')
    {
      curr->buf[nchars] = '\0';
      nchars++;
      /* XXX TODO record this */
    }
 
  /* update current buffer */
  curr->nchars = nchars;
  
  /* update total */
  lp->totchars += nchars;

  return;
}
  
#if 0
void 
dump_imgchars(http_trans_t *tp)
{
  il_imgchars_buf_t *buf = tp->il_imagechars.chain;

  if (buf == NULL)
    /* no chars */
    DUMP_INT(outp, 0UL, unsigned int);
  else
    {
      /* XXX TMP */
      unsigned int tot_here = 0UL;
      
      /* round off image chars buffer */
      close_imgchars(tp);
      

      assert(tp->il_imagechars.nbufs <= HTML_IMG_CHARS_MAX_BUFS);
      assert(tp->il_imagechars.totchars <= HTTP_IMAGECHARS_BUFLEN*HTML_IMG_CHARS_MAX_BUFS);

      DUMP_INT(outp, tp->il_imagechars.totchars, unsigned int);
      
      /* run through chain and dump */
      while (buf)
	{
	  tot_here += buf->nchars;	/* TMP */
	  DUMP_MEM(outp, buf->buf, buf->nchars);
	  buf = buf->next;
	}
      
      assert(tot_here == tp->il_imagechars.totchars);
    }
  
  return;
}
  

void 
dump_link_urls(http_trans_t *tp)
{
  il_imgchars_buf_t *buf = tp->link_urls.chain;

  if (buf == NULL)
    /* no chars */
    DUMP_INT(outp, 0UL, unsigned int);
  else
    {
      /* XXX TPM */
      unsigned int tot_here = 0UL;
      
      /* round off buffer */
      close_link_urls(tp);

      assert(tp->link_urls.nbufs <= (LINKS_MAX_BUFS + 1);
      assert (tp->link_urls.totchars <= LINKS_BUFSZ*LINKS_MAX_BUFS);
      DUMP_INT(outp, tp->link_urls.totchars, unsigned int);
      
      /* run through chain and dump */
      while (buf)
	{
	  tot_here += buf->nchars;	/* TMP */
	  DUMP_MEM(outp, buf->buf, buf->nchars);
	  buf = buf->next;
	}
      
      assert(tot_here == tp->link_urls.totchars);
    }
  
  return;
} 
#endif
  

void 
dump_link_urls(struct links_chars *lp)
{
  links_buf_t *buf = lp->chain;
  int nbufs = lp->nbufs;

  if (buf == NULL)
    /* no chars */
    DUMP_INT(outp, 0UL, unsigned int);
  else
    {
      /* XXX TPM */
      unsigned int tot_here = 0UL;
      
      /* round off buffer */
      close_link_urls(lp);

      assert(lp->nbufs <= (LINKS_MAX_BUFS + 1));
      assert (lp->totchars <= LINKS_BUFSZ*LINKS_MAX_BUFS);
      DUMP_INT(outp, lp->totchars, unsigned int);
      
      /* run through chain and dump */
      while (nbufs--)
	{
	  assert(buf->nchars <= LINKS_BUFSZ);
	  tot_here += buf->nchars;	/* TMP */
	  DUMP_MEM(outp, buf->buf, buf->nchars);
	  buf = buf->next;
	}
      
      assert(tot_here == lp->totchars);
    }
  
  return;
} 

void 
check_http_trans_fin(http_trans_t *tp, struct tcp_conn *tconnp,
		     int got_client, int got_server)
{
  
  /*
   * If the conection has just `disappeared' and a transaction is in progress
   * need to do some tidying up 
   */
  us_clock_t reqstart, reqend, repstart, repend;
  if (got_server)
    {
      repstart = tp->inner.sinf.repstart_us;
      repend = tp->inner.sinf.repend_us;
      if (repend == 0LL)
	{
	  /* Hasn't been completed gracefully */
	  unsigned short status = tp->inner.s.ti.status;
	  assert ((status & TRANS_INCOMPLETE) && !(status & TRANS_FINISHED));
	  tp->inner.s.ti.status |= TRANS_TIMING_PATCHED;
	  /* any data seen at all? */
	  if (tp->inner.first_sdata_pkt_us == 0LL) /* no data seen */
	    {
	      if (repstart != 0LL)
		{
		  assert (status & (TRANS_DUMMY_UNSYNCH | TRANS_DUMMY_ERR));
		  if (tconnp->tcp.server.lastdata_us != 0LL)
		    repend = tconnp->tcp.server.lastdata_us;
		  else
		    repend = repstart;
		}
	      else
		{
		  assert (tp->inner.s.ti.recd_len == 0UL);
		  if (tconnp->tcp.server.lastdata_us != 0LL)
		    repend = tconnp->tcp.server.lastdata_us;
		  else
		    repend = tconnp->tcp.server.rst_us;
		  repstart = repend;
		}
	    }
	  else
	    {
	      repend = tp->inner.sinf.repend_us;
	      assert(repstart != 0LL); 
	    }
	  
	  tp->inner.s.ti.status |= TRANS_OBJ_INTERRUPTED;
	}
      else
	{
	  assert(repstart != 0ULL); 
	}

      if (repstart > repend)
	{
	  repend = repstart;
	  tp->inner.s.ti.status |= TRANS_TIMING_PATCHED;
	}
      
    }
  
  if (got_client)
    {
      reqstart = tp->inner.cinf.reqstart_us;
      reqend = tp->inner.cinf.reqend_us;
      if (reqstart == 0LL)
	{
	  tp->inner.c.ti.status |= TRANS_TIMING_PATCHED;
	  assert (tp->inner.c.ti.recd_len == 0UL);
	  if (TCP_CFIRSTDATA_TM)
	    reqstart = TCP_CFIRSTDATA_TM;
	  else if (TCP_CLASTDATA_TM)
	    reqstart = TCP_CLASTDATA_TM;
	  else if (got_server)
	    reqstart = repstart;
	  else
	    reqstart = TCP_CSOLID_TM;
	}
      if (reqend == 0LL)
	reqend = reqstart;

      //assert(reqstart >= reqend);
      if (reqstart > reqend)
	{
	  reqend = reqstart;
	  tp->inner.c.ti.status |= TRANS_TIMING_PATCHED;
	}

      if (got_server &&  reqend > repstart)
	{
	  reqend = repstart;
	  if (reqend < reqstart)
	    reqstart = reqend;
	  tp->inner.c.ti.status |= TRANS_TIMING_PATCHED;
	}
    }
  
  tp->inner.cinf.reqstart_us = reqstart;
  tp->inner.cinf.reqend_us = reqend;
  tp->inner.sinf.repstart_us = repstart;
  tp->inner.sinf.repend_us = repend;
  
  return;
}

void 
do_http_counters(http_trans_t *tp)
{
  
  unsigned int sstatus = tp->inner.s.ti.status;
  unsigned int cstatus = tp->inner.c.ti.status;

  //assert ((cstatus & TRANS_VAL) || (sstatus & TRANS_VAL));

  if (!(sstatus & TRANS_VAL))
    {
      assert(cstatus & TRANS_VAL);
      if (!(cstatus & (TRANS_DUMMY_UNSYNCH | TRANS_DUMMY_ERR)))
	/* only seen client */
	BUMP_CTR(http_trans_cli_only);
    }
  else
    {
      if (!(sstatus & TRANS_INCOMPLETE))
	{
	  BUMP_CTR(http_trans_comp);
	}
      else if (sstatus & TRANS_FINISHED)
	{
	  BUMP_CTR(http_trans_fin);
	}
      else
	{
	  if (sstatus & (TRANS_DUMMY_UNSYNCH | TRANS_DUMMY_ERR))
	    {
	      BUMP_CTR(http_trans_dummy);
	    }
	  else if (sstatus & TRANS_LOST_SYNCH)
	    {
	      BUMP_CTR(http_trans_lost_synch);
	    }
	  else if (sstatus & TRANS_ERR)
	    {
	      BUMP_CTR(http_trans_err);
	    }
	  else
	    {
	      BUMP_CTR(http_trans_incomp);
	    }
	}
      if (sstatus & TRANS_RESYNCHED)
	BUMP_CTR(http_trans_resynch);
    }	
       
  if (!(cstatus & TRANS_VAL))
    {
      assert(sstatus & TRANS_VAL);
      if (!(sstatus & (TRANS_DUMMY_UNSYNCH | TRANS_DUMMY_ERR)))
	/* only seen client */
	BUMP_CTR(http_trans_serv_only);
    }
  else
    {
      if (cstatus & (TRANS_DUMMY_UNSYNCH | TRANS_DUMMY_ERR))
	{
	  BUMP_CTR(http_trans_dummy);
	}
      else if (cstatus & TRANS_LOST_SYNCH)
	{
	  BUMP_CTR(http_trans_lost_synch);
	}
      else if (cstatus & TRANS_ERR)
	{
	  BUMP_CTR(http_trans_err);
	}
      if (cstatus & TRANS_RESYNCHED)
	BUMP_CTR(http_trans_resynch);
    } 
  
  return;
}	

void 
tcp_http_dump(struct tcp_conn *tconnp)
{
  int i;
  int client_seen = TCP_STATE & TCP_CLIENT_SEEN;
  int server_seen = TCP_STATE & TCP_SERVER_SEEN;

  http_trans_t *tp;
  char *trans_write_start;


  rec_dump_start();

  /* tcp part */
  tcp_dump(tconnp, client_seen, server_seen);

  /* metadata on HTTP transactions */ 

  tconnp->su.http.meta.status |= HTTP_ADDITIONAL_FIELDS_PRESENT;
  DUMP_STRUCT(outp, &tconnp->su.http.meta, http_conn_meta_t);
  DUMP_INT(outp, (unsigned int)AF_ADDIT_FIELDS, unsigned int);

  /* then finally the data for each */
  trans_write_start = outp;
  for (i = 0, tp = tconnp->su.http.trans; i < HTTP_NTRANS; i++, tp = tp->next)
    {
      /* do the counters book-keeping */
      do_http_counters(tp);

      if (client_seen)
	{
	  DUMP_STRUCT(outp, &tp->inner.cinf, http_trans_cinf_t);
	  DUMP_STRUCT(outp, &tp->inner.c.ti, http_transinf_t);
	  if (tp->inner.cinf.reqlen)
	    {
	      assert (tp->inner.cinf.reqlen <= HTTP_REQSTR_LEN);
	      DUMP_MEM(outp, &tp->req, tp->inner.cinf.reqlen);
	    }
	  if (tp->inner.cinf.reflen)
	    {
	      assert (tp->inner.cinf.reflen <= HTTP_REQSTR_LEN);
	      DUMP_MEM(outp, &tp->ref, tp->inner.cinf.reflen);
	    }
	  if (tp->inner.cinf.hostlen)
	    {
	      assert (tp->inner.cinf.hostlen <= HTTP_REQSTR_LEN);
	      DUMP_MEM(outp, &tp->host, tp->inner.cinf.hostlen);
	    }
	  if (tp->inner.cinf.uagentlen)
	    {
	      assert (tp->inner.cinf.uagentlen <= HTTP_REQSTR_LEN);
	      DUMP_MEM(outp, &tp->uagent, tp->inner.cinf.uagentlen);
	    }
	  if (tp->inner.cinf.vialen)
	    {
	      assert (tp->inner.cinf.vialen <= HTTP_REQSTR_LEN);
	      DUMP_MEM(outp, &tp->cvia, tp->inner.cinf.vialen);
	    }
	}

      if (server_seen)
	{
#if 0
	  if(tp->inner.hserver.status & TRANS_VAL && tp->inner.status_code != 0 && (tp->inner.status_code < 100 || tp->inner.status_code > 600))
	    fprintf(stderr, "XXX funny status code %d\n", tp->inner.status_code);
#endif
	  DUMP_STRUCT(outp, &tp->inner.sinf, http_trans_sinf_t);
	  DUMP_STRUCT(outp, &tp->inner.s.ti, http_transinf_t);
	  DUMP_INT(outp, tp->inner.first_sdata_pkt_us, unsigned int);
	  DUMP_INT(outp, tp->inner.rep_hdr_len, int);

	  if (tp->inner.sinf.loclen)
	    {
	      assert (tp->inner.sinf.loclen <= HTTP_REQSTR_LEN);
	      DUMP_MEM(outp, &tp->loc, tp->inner.sinf.loclen);
	    }

	  if (tp->inner.sinf.serverlen)
	    {
	      assert (tp->inner.sinf.serverlen <= HTTP_REQSTR_LEN);
	      DUMP_MEM(outp, &tp->server, tp->inner.sinf.serverlen);
	    }
	  if (tp->inner.sinf.vialen)
	    {
	      assert (tp->inner.sinf.vialen <= HTTP_REQSTR_LEN);
	      DUMP_MEM(outp, &tp->svia, tp->inner.sinf.vialen);
	    }

	  if (tp->links.chain == NULL)
	    /* no chars */
	    DUMP_INT(outp, 0UL, unsigned int);
	  else
	    dump_link_urls(&tp->links);
	  
	}
      /* write could be large - so check if buffer management needed */
      if (outp -  trans_write_start > WRITE_BLKSZ)
	{ 
	  repbuf_manage(NO_CYCLE, NOT_CYCLE_SYNC, NOT_FINISHING);
	  trans_write_start = outp;
	}
    } /* end for */

  rec_dump_end(REC_TCP_HTTP);

  return;
}

void 
get_chunk_len(prec_t *pp, unsigned int *lenp, int *statep)
{
  unsigned int len = *lenp;
  
  while (pp->len)
    {
      char c = *pp->buf;
      if (c > '/' && c < ':')	/* dec digit */
	{
	  len *= 16;
	  len += c - '0';
	}
      else if (c > '`' && c < 'g') /* hex digit */
	{
	  len *= 16;
	  len += c - 'W';
	}
      else if (c > '@' && c < 'G') /* someone just has to use capitals */
	{
	  len *= 16;
	  len += c - '7';
	}
      else
	{
	  break;
	}
      
      ADJUST(pp, sizeof(char));
    }

  *lenp = len;

  if (pp->len)
    *statep = CHUNK_NO_STATE;

  return;
}

void 
clear_chunk_done(prec_t *pp, int *sp)
{
  unsigned char *tmpp;
  char delim[] = {'\r', '\n', '\r', '\n'};
  int indx = (*sp & CHUNKEND_INDX) >> 6;
  int adj;
  
  while (pp->len)
    {
      if (indx == 0)
	{
	  tmpp = seqchr(pp->buf, '\r', pp->len);
	  if (!tmpp)
	    {
	      ADJUST(pp, pp->len);
	      return;
	    }
	  adj = (int)(tmpp - pp->buf);
	  pp->buf += adj;
	  pp->len -= adj;
	}
      while (pp->len && delim[indx] == *pp->buf && indx++ < 4)
	{
	  ADJUST(pp, sizeof(char));
	}
      if (indx == 4)
	{
	  *sp = CHUNK_NO_STATE;
	  return;
	}
      else if (pp->len == 0)
	{
	  *sp = CHUNK_CHUNK_DONE | indx << 6;
	  return;
	}
      else
	{
	  indx = 0;
	  continue;
	}
    }
}


int 
body_de_chunk(prec_t *pp, tcp_conn_t *tconnp, short code, int way)
{
  unsigned int *chunk_left;
  int *chunk_statep;
  int *body_lenp;
  int err;

  if (way == SERVER)
    {
      chunk_left = &tconnp->su.http.reptrans->inner.s.chunk_left;
      chunk_statep = &tconnp->su.http.reptrans->inner.s.chunk_state;
      body_lenp = &tconnp->su.http.reptrans->inner.s.ti.body_len;
      err = HTTP_SERV_ERR_CHUNK;
    }
  else
    {
      chunk_left = &tconnp->su.http.reqtrans->inner.c.chunk_left;
      chunk_statep = &tconnp->su.http.reqtrans->inner.c.chunk_state;
      body_lenp = &tconnp->su.http.reqtrans->inner.c.ti.body_len;
      err = HTTP_CLI_ERR_CHUNK;
    }      
  
  while (pp->len)
    {

      switch (*chunk_statep & CHUNK_STATE_HIGH)
	{
	case CHUNK_CHUNK_DONE: goto chunk_done;
	case CHUNK_IN_LEN_DECODE: goto len_decode;
	case CHUNK_LEN_DECODED: goto len_decoded;
	case CHUNK_NO_STATE: break;
	}
      
      
      if (*chunk_left > 0)
	{
	  unsigned int chunk_to_do = MIN(pp->len, *chunk_left);
	  int status = way == SERVER ?
	    do_rep_body(pp, tconnp, chunk_to_do, code)
	    : parse_req_body(pp, tconnp, chunk_to_do);
	  if (status)
	    return status;
	  *chunk_left -= chunk_to_do;
	  if (!*chunk_left)
	    {
	      /* chunk finished - clear delimiter */
	      *chunk_statep = CHUNK_CHUNK_DONE | CHUNK_CLEAR_CR | CHUNK_CLEAR_NL;
	    chunk_done:
	      CLEAR_CHUNK_DELIM(pp, chunk_statep, err);
	    }
	  continue;
	}
      else
	{
	  int status;
	  /* get chunk size */
	  *chunk_statep = CHUNK_IN_LEN_DECODE;
	len_decode:
	  get_chunk_len(pp, chunk_left, chunk_statep);
	  if (!pp->len)
	    break;
	  if (*body_lenp == HTTP_BODY_LEN_UNKNOWN)
	    *body_lenp = 0;
	  *body_lenp += *chunk_left;
	  
	  if (*chunk_left > 0)
	    {
	      /* a chunk still to do */
	      *chunk_statep = CHUNK_LEN_DECODED | CHUNK_CLEAR_CR | CHUNK_CLEAR_NL | CHUNK_CLEAR_EXT;
	    len_decoded:
	      /* clear any EXT and delimiter */
	      CLEAR_CHUNK_DELIM(pp, chunk_statep, err);
	      
	      continue;
	    }
	  else
	    {
	      *chunk_statep = CHUNK_CHUNK_DONE;

	    done:
	      /* finished - clear delimiters and any footer */ 
	      clear_chunk_done(pp, chunk_statep);
	      trans_complete(tconnp, way);
	      
	    }
	}
    }
  
  return 0;
}

/*
 * Get text of request or reply header line, clear any trailing CR
 */

int 
get_hdrline(prec_t *pp, tcp_conn_t *tconnp, int way, char *top, unsigned char *charlen)
{
  unsigned char *cp;
  unsigned char *start;
  int len;
      

  CLEAR_SPACE(pp, way);
  cp = pp->buf;
  start = cp;
  len = MIN(HTTP_REQSTR_LEN-1, pp->len);

  while (*cp != '\n' && len > 0)
    {
      *(top++) = *(cp++);
      len --;
    }

  len = cp - start;

  /* clear any trailing CR */
  if (*(top-1) == '\r')
    {
      top--;
      len--;
    }
  
  /* clear any trailing spaces */
  while (len && *(top -1) == ' ')
    {
      top--;
      len--;
    }
  
  *charlen = len;

   if (cp - start >= pp->len)
     {
       return way == CLIENT ? HTTP_CLI_ERR_TRUNCHDR : HTTP_SERV_ERR_TRUNCHDR;
     }
   else
     {
       ADJ_BUF(pp, cp);
       CLEAR_LINE_RET(pp, way);
     }
   
 
  return 0;
}

int  
save_interrupted_hdr(char *start, int len, http_hdr_parse_state_t *ps, int way)
{
  int coplen;
  
  if (ps->buffer == NULL)
    {
      assert(ps->reallen == 0);
      ps->buffer = get_saved_hdr_buffer();
      BUMP_CTR(frag_hdr_trans);
    }

  BUMP_SPECIFIC_OCTS(frag_hdr_pkts, len);

  if (++(ps->ncop) > HTTP_HDR_MAX_NCOP)
    /* too fragmented */
    return way == SERVER ? HTTP_SERV_ERR_HDRCOP : HTTP_CLI_ERR_HDRCOP;

  if ((coplen = MIN(len, HTTP_HDR_BUFSZ - ps->reallen)) <= 0)
    /* run out of room */
    return  way == SERVER ? HTTP_SERV_ERR_HDRBUF : HTTP_CLI_ERR_HDRBUF;

  memcpy(&ps->buffer->buff[ps->reallen], start, coplen);
  ps->reallen += coplen;

  assert(ps->reallen >= 0);

  /* parse is always started from scratch */
  ps->buf.buf = ps->buffer->buff;
  ps->buf.len = ps->reallen;
  

  return 0;
}

int 
get_encoding_type(prec_t *pp, tcp_conn_t *tconnp, int way, char *dummy, unsigned char *anotherdummy) 
{
  int status;

  CLEAR_COMMA(pp);
  CLEAR_SPACE(pp, way);
  
  switch (*(pp->buf))
    {
    case 'c':
    case 'C':  
      status =  ci_seqstrncmp(pp->buf, "chunked", pp->len);
      if (status > 0)
	{
	  ADJUST(pp, status);
	  if (way == SERVER)
	    HTTP_REP_TRANS_STATUS |= TRANS_CHUNKED;
	  else
	    HTTP_REQ_TRANS_STATUS |= TRANS_CHUNKED;
	  BUMP_CTR(http_trans_chunked);
	}
      else if (status < 0)
	{
	  return way == SERVER ? HTTP_SERV_ERR_TRUNCHDR : HTTP_CLI_ERR_TRUNCHDR;
	}
      break;
      
    default:
      break;
    }

  CLEAR_LINE_RET(pp, way);
  
  return 0;
}

int 
get_trailer_fields(prec_t *pp, tcp_conn_t *tconnp, int way, char *dummy, unsigned char *anotherdummy) 
{

  /* XXX TODO - get actual fields */
  CLEAR_COMMA(pp);
  CLEAR_SPACE(pp, way);

  BUMP_CTR(http_trans_trailer);
  INTERESTING_NL(&tconnp->flow_common.inner, way, pp, "#%u Trailer fields: ", tconnp->hdrs.conn_id);

  CLEAR_LINE_RET(pp, way);
  return 0;

}

int 
get_content_length(prec_t *pp, tcp_conn_t *tconnp, int way, char *dummy, unsigned char *anotherdummy)
{
  int len = 0;
  int ret;
  unsigned int *lenp = (way == SERVER ? &HTTP_REP_BODY_LEN : &HTTP_REQ_BODY_LEN);

  CLEAR_SPACE(pp, way);
  while (isdigit(*(pp->buf)))
    {
      len *= 10;
      len += (*(pp->buf) - '0');
      ADJUST(pp, sizeof(char));
    }
  if (len >= 0)
    {
      *lenp = len;
      ret = 0;
    }
  else
    {
      ret =  way == SERVER ? HTTP_SERV_ERR_LEN :  HTTP_CLI_ERR_LEN;
    }

  CLEAR_LINE_RET(pp, way);
  
  return  ret;
}

int 
get_http_version(prec_t *pp, int way)
{
  int vers = -1;
  int len = strlen("HTTP/x.x");

  if (pp->len < len)
    return way == SERVER ? HTTP_SERV_ERR_TRUNCHDR : HTTP_CLI_ERR_TRUNCHDR;

  if (!memcmp(pp->buf, "HTTP/1.0", len))
    {
      vers =  HTTP_VERS_1_0;
    }
  else if (!memcmp(pp->buf, "HTTP/1.1", len))
    {
      vers = HTTP_VERS_1_1;
    }

  if (vers > 0)
    ADJUST(pp, len);
  else 
    vers = way == SERVER ? HTTP_SERV_ERR_VERS : HTTP_CLI_ERR_VERS;

  return vers;
}

int 
get_connection_type(prec_t *pp, tcp_conn_t *tconnp, int way, char *dummy, unsigned char *anotherdummy) 
{
  int status;
  
  CLEAR_SPACE(pp, way);
  
  status =  ci_seqstrncmp(pp->buf, "close", pp->len);
  if (status > 0)
    {
      ADJUST(pp, status);
      HTTP_STATUS |= 
	 (way == SERVER ? HTTP_SERV_CLOSE : HTTP_CLI_CLOSE);
      HTTP_STATUS &= 
	(way == SERVER ? ~HTTP_SERV_KEEP_ALIVE : ~HTTP_CLI_KEEP_ALIVE);
    }
  else if (status == 0)
    {
      status =  ci_seqstrncmp(pp->buf, "keep-alive", pp->len);
      if (status > 0)
	{
	  ADJUST(pp, status);
	  HTTP_STATUS |= 
	    (way == SERVER ? HTTP_SERV_KEEP_ALIVE : HTTP_CLI_KEEP_ALIVE);
	  HTTP_STATUS &= 
	    (way == SERVER ? ~HTTP_SERV_CLOSE : ~HTTP_CLI_CLOSE);
	}
      else if (status < 0)
	{
	  return way = SERVER ? HTTP_SERV_ERR_TRUNCHDR : HTTP_CLI_ERR_TRUNCHDR;
	}
    }
  else if (status < 0)
    {
      return way = SERVER ? HTTP_SERV_ERR_TRUNCHDR : HTTP_CLI_ERR_TRUNCHDR;
    }

  if (HTTP_STATUS & HTTP_SERV_KEEP_ALIVE
      && HTTP_STATUS &  HTTP_CLI_KEEP_ALIVE)
    {
      HTTP_STATUS |=  (HTTP_KEEP_ALIVE | HTTP_WAS_PERSISTENT);
      HTTP_STATUS &=  ~HTTP_CLOSE;
    }

  if (HTTP_STATUS & HTTP_SERV_CLOSE
      || HTTP_STATUS & HTTP_CLI_CLOSE)
    {
      HTTP_STATUS |=  HTTP_CLOSE;
      HTTP_STATUS &=  ~HTTP_KEEP_ALIVE;
    }

  if (IS_PERSISTENT_CONN && IS_NON_PERSISTENT_CONN)
    return HTTP_ERR_PERSISTANCE;

  CLEAR_LINE_RET(pp, way);

  return 0;
}

int
new_http_trans(tcp_conn_t *tconnp, int way)
{
  http_trans_t *newtransp, **curtransp;

  if (HTTP_NTRANS >= MAX_NTRANS)
    return way == SERVER ? HTTP_SERV_ERR_TRANS_EXC : HTTP_CLI_ERR_TRANS_EXC;

  curtransp = way == CLIENT ? 
		&tconnp->su.http.reqtrans : &tconnp->su.http.reptrans;
  
  if (*curtransp)
    {
      if ((*curtransp)->next)
	{
	  *curtransp = (*curtransp)->next;
	  goto mark_valid;
	}
    }
  else if (tconnp->su.http.trans)
    {
      *curtransp = tconnp->su.http.trans;
      goto mark_valid;
    }

  newtransp = get_http_trans_t();
  assert(newtransp->next == NULL);

  HTTP_NTRANS++;
  BUMP_CTR(http_trans);
  
  if (*curtransp)
    {
      (*curtransp)->next = newtransp;
      *curtransp = newtransp;
      goto mark_valid;

    }

  tconnp->su.http.trans = newtransp;
  *curtransp = newtransp;

 mark_valid:
  
  if (way == SERVER)
    {
      (*curtransp)->inner.s.ti.status |= TRANS_VAL;
    }
  else
    {
      (*curtransp)->inner.c.ti.status |= TRANS_VAL;
      /*
       * There's a nasty situation that can arise - we've come across a new 
       * client transaction, but it is already in progress from the server end.
       * A likely scenario is a combination of lost/missed packets together 
       * with TCP implementations which re-use the last initial sequence 
       * number - yes there are some out there.
       */
      if (((*curtransp)->inner.s.ti.status & TRANS_VAL)
	  && !(((*curtransp)->inner.s.ti.status & (TRANS_DUMMY_UNSYNCH | TRANS_DUMMY_ERR)) || ((*curtransp)->inner.s.ti.error)))
	    {
	      /* server already delivering */
	      (*curtransp)->inner.cinf.reqstart_us = TCP_CSOLID_TM;
	      return HTTP_CLI_SERVER_TRANS_ACTIVE;
	    }
    }

  assert (((*curtransp)->inner.s.ti.status & TRANS_VAL) || ((*curtransp)->inner.c.ti.status & TRANS_VAL));

  return 0;
}

void 
trans_complete(tcp_conn_t *tconnp, int way)
{
  
  http_transinf_t *ti;

  if (way == CLIENT)
    {
      ti = &tconnp->su.http.reqtrans->inner.c.ti;
      HTTP_STATUS &= ~HTTP_GOT_REQ_HDR;
      HTTP_LASTREQ_SEEN = TCP_CSOLID_TM;
    }
  else
    {
      ti = &tconnp->su.http.reptrans->inner.s.ti;
      HTTP_STATUS &= ~HTTP_GOT_REP_HDR;
      HTTP_LASTREP_SEEN = TCP_SSOLID_TM;

      if (HTTP_SERV_STATUS == 200)
	{
	  if (ti->status & TRANS_RESYNCHED)
	    {
	      ti->recd_len -= ti->gaps_bytes;
	    }
	  else
	    {
	      ti->status &= ~TRANS_INCOMPLETE;
	      fingerprint_final(&tconnp->su.http.reptrans->inner.sinf.finger);
	    }
	}
      else
	{
	  ti->status |= TRANS_FINISHED;
	}
    }
  
  assert (ti->status & TRANS_VAL);
  
  return;
}

void 
free_trans(tcp_conn_t *tconnp)
{
  http_trans_t *tp = tconnp->su.http.trans;
  
  while(tp)
    {
      http_trans_t *gone = tp;
      links_buf_t *lp = tp->links.chain;
      tp = tp->next;
      
      /* free buffer chains */

      while(lp != NULL)
	{
	  links_buf_t *tmpp = lp;
	  lp = lp->next;
	  recycle_links_buffer(tmpp);
	}

      if(gone->inner.dump_fd > 0)
	{
	  /* saving object to file */
	  if (close(gone->inner.dump_fd) != 0)
	    error("free trans", "close");
	}
      
      
      recycle_http_trans_t(gone);
    }
  
  return;
}

/*
 * Attempt to resynch HTTP after missing TCP sequence 
 * - return 1 if successful 
 */

int 
tcp_http_sync(tcp_heldpkt_t *hpp, tcp_conn_t *tconnp, tcp_simplex_flow_t *tsp, 
	     unsigned int gap, int way, unsigned int tm)
{
  http_transinf_t *ti;
  int got_hdr;
  int got_trans = (int)
    (way == CLIENT ? tconnp->su.http.reqtrans : tconnp->su.http.reptrans);

  if (!got_trans)
    goto cant_synch_notrans;

  ti = (way == CLIENT ? 
	&tconnp->su.http.reqtrans->inner.c.ti 
	: &tconnp->su.http.reptrans->inner.s.ti);

  if (ti->status & TRANS_DUMMY_UNSYNCH)
    /* already lost synch - record this gap */
    {
      ti->gaps_bytes += gap;
      ti->gaps_pkts++;
      return 0;
    } 

  got_hdr = HTTP_STATUS & 
    (way == CLIENT ? HTTP_GOT_REQ_HDR : HTTP_GOT_REP_HDR);

  if (!got_hdr)
    goto cant_synch;

  /* TODO - handle chunked transfers */
#if 0
  if ((way == CLIENT && (HTTP_REQ_TRANS_STATUS & TRANS_CHUNKED))
      || (way == SERVER && (HTTP_REP_TRANS_STATUS & TRANS_CHUNKED)))
    goto cant_synch;
#endif

  if (ti->status & TRANS_CHUNKED)
    goto cant_synch;

  /* easy case */
  if (ti->body_len != HTTP_BODY_LEN_UNKNOWN 
      && gap <= (ti->body_len - ti->recd_len))
    goto can_synch;

  /* DROP_THROUGH */

 cant_synch:
  ti->status |= TRANS_LOST_SYNCH; /* mark old */
  if (way == SERVER)
    HTTP_LASTREP_SEEN = tm;
  else
    HTTP_LASTREQ_SEEN = tm;
    
 cant_synch_notrans:
  new_http_trans(tconnp, way);	/* set up dummy to collect remainder */
  HTTP_NTRANS_DUM++;
  if (way == CLIENT)
    {
      HTTP_FIRSTREQ_SEEN = tm;
      HTTP_LASTREQ_SEEN = tm;
      HTTP_REQ_TRANS_STATUS |= TRANS_DUMMY_UNSYNCH;
      HTTP_REQ_CONTENT_T = CT_UNSYNCH;
    }
  else
    {
      HTTP_FIRSTREP_SEEN = tm;
      HTTP_LASTREP_SEEN = tm;
      HTTP_LASTDATA_SEEN = tm;
      HTTP_REP_TRANS_STATUS |= TRANS_DUMMY_UNSYNCH;
      HTTP_REP_CONTENT_T = CT_UNSYNCH;
    }
  
  return 0;

 can_synch:
  ti->status |= TRANS_RESYNCHED; /* mark it */
  ti->gaps_bytes += gap;	/* record size */
  ti->gaps_pkts++;
  ti->recd_len += gap;		/* so completion triggered - adjust then */
  return 1;
}

    
      



int 
do_client_pkt(prec_t *pp, tcp_conn_t *tconnp)
{
  int pkt_counted = 0;

  /* first check for error or unsynched condition and record packet */
  if (tconnp->su.http.reqtrans)
    { 
      HTTP_REQ_RECD_PKTS++;
      pkt_counted++;
      if (HTTP_REQ_TRANS_STATUS & (TRANS_DUMMY_UNSYNCH | TRANS_DUMMY_ERR))
	{
	  HTTP_REQ_RECD_LEN += pp->len;
	  HTTP_LASTREQ_SEEN = TCP_CSOLID_TM;
	  return HTTP_ERR_NONE;
	}
    }
  
  /* now deal with ok packet */
  
  while (pp->len)
    {
      if (!(HTTP_STATUS & HTTP_GOT_REQ_HDR))
	{
	  int status;
	  
	  if (!(HTTP_STATUS & HTTP_IN_REQ_HDR))
	    {
	      if (HTTP_STATUS & HTTP_SEEN_REQ_HDR)
		{
		  /* another request - confirm is persistant connection */
		  if (IS_NON_PERSISTENT_CONN)
		    return HTTP_CLI_ERR_PERSISTANCE;
		  HTTP_STATUS |= (HTTP_KEEP_ALIVE | HTTP_WAS_PERSISTENT);
		}
	      status = new_http_trans(tconnp, CLIENT);
	      if (!pkt_counted)
		{ 
		  HTTP_REQ_RECD_PKTS++;
		  pkt_counted++;
		}
	      if (status)
		return status;
	      HTTP_FIRSTREQ_SEEN = TCP_CSOLID_TM;
	      HTTP_LASTREQ_SEEN = TCP_CSOLID_TM;
	    }
	  status = parse_req_hdr(pp, tconnp, NOT_RECURSING);
	  if (status)
	    return status;
	}

      /* Any body?
       * - if so must be flagged by chunking or body len hdr field 
       *   and we should have picked that up from the header 
       */

      if (HTTP_REQ_TRANS_STATUS & TRANS_CHUNKED)
	{
	  /* Got a chunked transfer encoding */
	  int status = body_de_chunk(pp, tconnp, 0, CLIENT);
	  if (status)
	    return status;
	}

      else if (HTTP_REQ_BODY_LEN)
	{
	  int body_to_do = MIN(HTTP_REQ_BODY_LEN-HTTP_REQ_RECD_LEN, pp->len);

	  assert (body_to_do >= 0);

	  /* any others mandatory no body? */
	  if (HTTP_METH == HTTP_METHOD_GET 
	      || HTTP_METH == HTTP_METHOD_HEAD
	      || HTTP_METH == HTTP_METHOD_OPTIONS
	      || HTTP_METH == HTTP_METHOD_DELETE)
	    return HTTP_CLI_ILLICIT_BODY;

	  if (body_to_do)
	    {
	      int status =  parse_req_body(pp, tconnp, body_to_do);
	      if (status)
		return status;
	    }

	  if (HTTP_REQ_RECD_LEN == HTTP_REQ_BODY_LEN)
	    trans_complete(tconnp, CLIENT);

	}

      else 
	/* no body */
	{
	  trans_complete(tconnp, CLIENT);
	} 
      
    } /* end while() */
  
  return 0;
}

int 
do_server_pkt(prec_t *pp, tcp_conn_t *tconnp)
{
  /* to get here the packet must contain some data */
  int pkt_counted = 0;

  /* first check for error or unsynched condition and record packet */
  if (tconnp->su.http.reptrans)
    { 
      HTTP_REP_RECD_PKTS++;
      pkt_counted++;
      HTTP_LASTREP_SEEN = TCP_SSOLID_TM;
      /* HTTP_LASTDATA_SEEN = TCP_SSOLID_TM; - now same as previous line */
      if (HTTP_REP_TRANS_STATUS & (TRANS_DUMMY_UNSYNCH | TRANS_DUMMY_ERR))
	{
	  HTTP_REP_RECD_LEN += pp->len;
	  HTTP_LASTREP_SEEN = TCP_SSOLID_TM;
	  return HTTP_ERR_NONE;
	}
    }
      

  /* now deal with ok packet */
  while (pp->len > 0)
    {
      if (!(HTTP_STATUS & HTTP_GOT_REP_HDR))
	{
	  int status;
	  
	  if (!(HTTP_STATUS & HTTP_IN_REP_HDR))
	    {
	      if (HTTP_STATUS & HTTP_SEEN_REP_HDR)
		{
		  /* another response - confirm is persistant connection */
		  if (IS_NON_PERSISTENT_CONN)
		    return HTTP_SERV_ERR_PERSISTANCE;
		  HTTP_STATUS |= (HTTP_KEEP_ALIVE | HTTP_WAS_PERSISTENT);
		}
	      status = new_http_trans(tconnp, SERVER);
	      if (!pkt_counted)
		{ 
		  HTTP_REP_RECD_PKTS++;
		  pkt_counted++;
		}
	      if (status)
		return status;
	      HTTP_FIRSTREP_SEEN = TCP_SSOLID_TM;
	      HTTP_LASTREP_SEEN = TCP_SSOLID_TM;
	      /* HTTP_LASTDATA_SEEN = TCP_SSOLID_TM; - now same as previous line */
	      /* catch no-body reponses */
	      HTTP_FIRSTDATA_SEEN = TCP_SSOLID_TM;
	    }
	  status = parse_rep_hdr(pp, tconnp, NOT_RECURSING);
	  if (status)
	    return status;
	}
    
      if (HTTP_REP_TRANS_STATUS & TRANS_CHUNKED)
	{
	  /* Got a chunked transfer encoding */
	  int status = body_de_chunk(pp, tconnp, HTTP_SERV_STATUS, SERVER);
	  if (status)
	    return status;
	}
      else
	/* Not chunked */
	{
	  if (IS_PERSISTENT_CONN)
	    /* definitely persistent connection */
	    {
	      if (HTTP_REP_BODY_LEN == HTTP_BODY_LEN_UNKNOWN)
		return HTTP_SERV_ERR_UBL;
	    }
	  else
	    /* may be either */
	    {
	      if (HTTP_REP_BODY_LEN == HTTP_BODY_LEN_UNKNOWN)
		/* must be non-persistent or error */
		{
		  int status = do_rep_body(pp, tconnp, pp->len, HTTP_SERV_STATUS);
		  if (status)
		    return status;
		  break;
		}
	      else
		/* know body length */
		{
		  if (HTTP_STATUS & HTTP_GOT_REQ_HDR)
		    /* know must be non-persistent */
		    {
		      if ((pp->len + HTTP_REP_RECD_LEN) > HTTP_REP_BODY_LEN)
			{
			  /* have over-run - parse expected body */
			  int status = do_rep_body(pp, tconnp, 
						      HTTP_REP_BODY_LEN - HTTP_REP_RECD_LEN, HTTP_SERV_STATUS);
			  if (status)
			    return status;
			  else
			    /* got it all - tidy up */
			    trans_complete(tconnp, SERVER);

			  return HTTP_SERV_ERR_BODY_TOO_LONG;
			}
		      else
			{
			  /* parse as much of body as we have */
			  int status = 
			    do_rep_body(pp, tconnp, pp->len, HTTP_SERV_STATUS);
			  if (status)
			    return status;
			  break;
			}
#if 0
		      if (tconnp->tcp.common.state & TCP_SERV_FIN)
			{
			  /*  got it all - tidy up */
			  trans_complete(tconnp, SERVER);
			}
#endif
		    }
		}
	    }
	  
	  /* 
	   * We have now got an appropriate header and have dealt with any 
	   * body part on a known non-persistent connection 
	   * - now process on assumption of persistence 
	   */
	  
	  for (;;)
	    {
	      
	      int body_len_remaining = HTTP_REP_BODY_LEN - HTTP_REP_RECD_LEN;
	      int body_len_todo = MIN(body_len_remaining, pp->len);
	      
	      if (body_len_remaining)
		{
		  if (body_len_todo)
		    {
		      int status = 
			do_rep_body(pp, tconnp, body_len_todo, HTTP_SERV_STATUS);
		      if (status)
			return status;
		    }
		  else
		    {
		      break;
		    }
		}
	      else
		{
		  /*  got it all - tidy up */
		  trans_complete(tconnp, SERVER);
		  break;
		}
	    } /* end for(;;) */
	} /* end not chunked */
      
    } /* end while() */
  
  
  return 0;
}

	   
/* ASSUME that by here TCP level has ordered pkts and adjusted for holes */
int 
tcp_http_pkt(prec_t *pp, tcp_conn_t *tconnp, int way)
{
  int status;			/* <0 -> error, >0 failure */

#ifdef PRINT_OUT
  char *http_start = pp->buf;
  int http_len = pp->len;
#endif



  if (way == CLIENT)
    TIME_NP_CALL_RET("TCP http pkt", &call_times.http, 
		     do_client_pkt(pp, tconnp));
  else
    TIME_NP_CALL_RET("TCP http pkt", &call_times.http, 
		     do_server_pkt(pp, tconnp));
      
#ifdef PRINT_OUT
  if (print_packets)
#ifdef TCPDUMP_FED
    print_http(http_start, http_len, pp->trunc, tconnp, repfile, way);
#else
  print_http(http_start, http_len, tconnp, repfile, way);
#endif
#endif

  if (status < 0)
    http_error(tconnp, pp, status, way, NULL);
  
  return status;
}

tcp_serv_methods_t tcp_http_serv_methods = 
{
  tcp_http_open,
  tcp_http_pkt,
  tcp_http_sync,
  tcp_http_close,
  tcp_http_dump
};

serv_control_t tcp_http_serv_control = 
  {
    {
      &tcp_http_serv_methods
    },
    {
      REC_TCP_HTTP_OPEN, REC_TCP_HTTP_HDRS, REC_TCP_HTTP, TCP_SERV_HTTP
    },
    &counters.TRAFF_TCP_HTTP,
    DUMP_HDRS
  };
      


/*
 * end http.c 
 */
