/*  -*- 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 <strings.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.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 <unistd.h>

#include <linux/limits.h>

#include <assert.h>

#include "list.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 "icmp.h"
#include "print_util.h"
#include "procstat.h"
#include "sundry_records.h"
#include "if_stats.h"
#include "report.h"
#include "output.h"
#include "counters.h"
#include "writer.h"
#include "wread_util.h"
#include "http_util.h"
#include "content_t.h"

//#include "probe_config.h"

#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define MAX(x, y) ((x) > (y) ? (x) : (y))

#if defined FINAL_REPORT || defined REPORT
int report = 0;
#endif /* defined FINAL_REPORT || defined REPORT */

/*
 * HTTP stuff 
 */

void 
print_trans(FILE *f, http_trans_t *tp, int indx, int client_seen, 
	    int server_seen, unsigned int addit_fields, us_clock_t start)
{
  int non_prints = 0;
  int need_nl = 0;

#if 0
  printf("%u\n%u\n%u\n%u\n(0 %u %u %u) \n", 
	 tp->inner.cinf.reqstart_us, 
	 tp->inner.cinf.reqend_us, 
	 tp->inner.sinf.repstart_us,
	 tp->inner.sinf.repend_us,
	 tp->inner.cinf.reqend_us - tp->inner.cinf.reqstart_us,
	 tp->inner.sinf.repstart_us - tp->inner.cinf.reqend_us,
	 tp->inner.sinf.repend_us - tp->inner.sinf.repstart_us);
#endif
  
  if (indx >= 0)
    REP(f, "  Transaction #%d\n", indx);

  if (client_seen)
    {
      REP(f, "    Req %s - ", 
	  us_clock_ts_string(tp->inner.cinf.reqstart_us + start));
      REP(f, "%s = %dms (%dus)\n",  
	  us_clock_ts_string(tp->inner.cinf.reqend_us + start),
	  (tp->inner.cinf.reqend_us-tp->inner.cinf.reqstart_us)/US_IN_MS,
	  tp->inner.cinf.reqend_us-tp->inner.cinf.reqstart_us);
    }
  if (server_seen)
    {
      REP(f, "    Rep %s - ", 
	  us_clock_ts_string(tp->inner.sinf.repstart_us + start));
      REP(f, "%s = %dms (%dus)\n", 
	  us_clock_ts_string(tp->inner.sinf.repend_us + start),
	  (tp->inner.sinf.repend_us-tp->inner.sinf.repstart_us)/US_IN_MS,
	  tp->inner.sinf.repend_us-tp->inner.sinf.repstart_us);
      if (addit_fields & AF_FIRST_REP_DATA_TM)
	{
	  REP(f, "    (Body %+dms (%+dus))\n",
	      (tp->inner.first_sdata_pkt_us-tp->inner.sinf.repstart_us)/US_IN_MS,
	  tp->inner.first_sdata_pkt_us-tp->inner.sinf.repstart_us);
	}
	      
    }
  if (client_seen && server_seen)
    REP(f, "    Req end - Rep start = %dms (%dus)\n",
	(tp->inner.sinf.repstart_us-tp->inner.cinf.reqend_us)/US_IN_MS,
	tp->inner.sinf.repstart_us-tp->inner.cinf.reqend_us);
  
  if (client_seen)
    {
      if (!(tp->inner.hclient.status & TRANS_VAL))
	{
	  REP(f, "    Client transaction invalid\n");
	  goto c_inval;
	}
      if (tp->inner.hclient.status & TRANS_ERR)
	REP(f, "    Error %d %s\n", 
	    tp->inner.hclient.error, http_err_string(tp->inner.hclient.error));
      if (tp->inner.hclient.status & TRANS_DUMMY_UNSYNCH)
	{
	  REP(f, "    Unsynchronised: %u/%u from client (start %u)\n",
	      tp->inner.hclient.recd_len, tp->inner.hclient.recd_pkts, 
	      tp->inner.cinf.reqstart_us);
	  if (tp->inner.hclient.gaps_bytes)
	    REP(f, "    missing: %u/%u\n", tp->inner.hclient.gaps_bytes, 
		tp->inner.hclient.gaps_pkts);
	}
      else if (tp->inner.hclient.status & TRANS_DUMMY_ERR)
	{
	  REP(f, "    Following error: %u%u from client\n",
	      tp->inner.hclient.recd_len, tp->inner.hclient.recd_pkts);
	}
      else
	{
	  REP(f, "    Method: %s ", method_string(tp->inner.cinf.method));
	  if (tp->inner.cinf.reqlen || 1)
	    {
	      REP(f, "Object ");
	      non_prints = print_seq2str(f, tp->req, tp->inner.cinf.reqlen);
	    }
	  REP(f, "\n");
	  //if (tp->inner.hclient.content_type != CT_UNKNOWN)
	  REP(f, "      Content type %s ", 
		content_type_string(tp->inner.hclient.content_type));
	  if (tp->inner.hclient.recd_len || tp->inner.hclient.recd_pkts)
	    REP(f, " %u of %d in %u pkts ", tp->inner.hclient.recd_len,
		tp->inner.hclient.body_len, tp->inner.hclient.recd_pkts);
	      
	  REP(f, " %s %s %s %s %s",  
	      tp->inner.hclient.status & TRANS_CHUNKED ? "Chunked" : "",
	      tp->inner.hclient.status & TRANS_INCOMPLETE ? "Incomplete" : "", 
	      tp->inner.hclient.status & TRANS_RESYNCHED ? "Resynched" : "", 
	      tp->inner.hclient.status & TRANS_LOST_SYNCH ? "Lost synch" : "",
	      tp->inner.hclient.status & TRANS_HDR_FRAG ? "Hdr. frag." : "");
	  REP(f, "\n");
	  if (non_prints)
	    REP(f, "XXX Caution - request contains non-printing characters XXX\n");
	  if (tp->inner.cinf.hostlen)
	    {
	      REP(f, "      Host: ");
	      non_prints = print_seq2str(f, tp->host, tp->inner.cinf.hostlen);
	      REP(f, "\n");
	      if (non_prints)
		REP(f, "XXX Caution - reference contains non-printing characters XXX\n");
	    }
	  if (tp->inner.cinf.reflen)
	    {
	      REP(f, "      Referer: ");
	      non_prints = print_seq2str(f, tp->ref, tp->inner.cinf.reflen);
	      REP(f, "\n");
	      if (non_prints)
		REP(f, "XXX Caution - reference contains non-printing characters XXX\n");
	    }
	  if (tp->inner.cinf.uagentlen)
	    {
	      REP(f, "      User agent: ");
	      non_prints = print_seq2str(f, tp->uagent, tp->inner.cinf.uagentlen);
	      REP(f, "\n");
	      if (non_prints)
		REP(f, "XXX Caution - reference contains non-printing characters XXX\n");
	    }

	  if (tp->inner.cinf.vialen)
	    {
	      REP(f, "      Via: ");
	      non_prints = print_seq2str(f, tp->cvia, tp->inner.cinf.vialen);
	      REP(f, "\n");
	      if (non_prints)
		REP(f, "XXX Caution - reference contains non-printing characters XXX\n");
	    }
	  
	  if (tp->inner.hclient.gaps_bytes)
	    REP(f, "    missing: %u/%u\n", tp->inner.hclient.gaps_bytes, 
		tp->inner.hclient.gaps_pkts);
	}
    }

 c_inval:
  
  if (server_seen)
    {
      if (!(tp->inner.hserver.status & TRANS_VAL))
	{
	  REP(f, "    Server transaction invalid\n");
	  goto s_inval;
	} 
      
      if (tp->inner.hserver.status & TRANS_ERR)
	REP(f, "    Error %d %s\n", tp->inner.hserver.error, http_err_string(tp->inner.hserver.error));
      
      if (tp->inner.hserver.status & TRANS_DUMMY_UNSYNCH)
	{
	  REP(f, "    Unsynchronised: %u/%u from server (%u - %u)\n",
	      tp->inner.hserver.recd_len, tp->inner.hserver.recd_pkts,
	      tp->inner.sinf.repstart_us, tp->inner.sinf.repend_us);
	  if (tp->inner.hserver.gaps_bytes)
	    REP(f, "    missing: %u/%u\n", tp->inner.hserver.gaps_bytes, 
		tp->inner.hserver.gaps_pkts);
	}
      else if (tp->inner.hserver.status & TRANS_DUMMY_ERR)
	{
	  REP(f, "    Following error: %u/%u from server\n",
	      tp->inner.hserver.recd_len, tp->inner.hserver.recd_pkts);
	}
      else
	{
	  //t1 = (float)tp->inner.req_us;
	  //t2 = (float)tp->inner.repstart_us;
	  //t3 = (float)tp->inner.repend_us;

	  REP(f, "    Reply: type %s %u of %d in %u pkts %s %s %s %s %s %s\n      finger %08x:%08x %u\n", 
	      content_type_string(tp->inner.hserver.content_type), 
	      tp->inner.hserver.recd_len, tp->inner.hserver.body_len,
	      tp->inner.hserver.recd_pkts, 
	      tp->inner.hserver.status & TRANS_CHUNKED ? "Chunked" : "",
	      tp->inner.hserver.status & TRANS_INCOMPLETE ? "Incomplete" : "",
	      tp->inner.hserver.status & TRANS_FINISHED ? "Finished" : "", 
	      tp->inner.hserver.status & TRANS_RESYNCHED ? "Resynched" : "", 
	      tp->inner.hserver.status & TRANS_LOST_SYNCH ? "Lost synch" : "",
	      tp->inner.hserver.status & TRANS_HDR_FRAG ? "Hdr. frag." : "",
	      tp->inner.sinf.finger.hi, tp->inner.sinf.finger.lo, tp->inner.sinf.finger.tot);

	  if (addit_fields & AF_REP_HDR_LEN)
	    REP(f, "     (HTTP hdr len %d)\n", tp->inner.rep_hdr_len);

	  if (tp->inner.sinf.serverlen)
	    {
	      REP(f, "      Server: ");
	      non_prints = print_seq2str(f, tp->server, tp->inner.sinf.serverlen);
	      REP(f, "\n");
	      if (non_prints)
		REP(f, "XXX Caution - reference contains non-printing characters XXX\n");
	    }

	  if (tp->inner.sinf.vialen)
	    {
	      REP(f, "      Via: ");
	      non_prints = print_seq2str(f, tp->svia, tp->inner.sinf.vialen);
	      REP(f, "\n");
	      if (non_prints)
		REP(f, "XXX Caution - reference contains non-printing characters XXX\n");
	    }
#if 0
	  REP(f, "    Req. seen %.3f req.end %.3f first served %.3f last served %.3f\n",
	     ((float)tp->inner.cinf.reqstart_us)/US_IN_MS,
	     ((float)tp->inner.cinf.reqend_us)/US_IN_MS, 
	      ((float)tp->inner.sinf.repstart_us)/US_IN_MS, 
	      ((float)tp->inner.sinf.repend_us)/US_IN_MS);
#endif
	  REP(f, "      Status: %hd %s ", tp->inner.sinf.status_code, 
	      status_string(tp->inner.sinf.status_code));

	  if (client_seen && tp->inner.cinf.reqstart_us != 0)
	    REP(f, "%.3f/", 
		((float)(tp->inner.sinf.repstart_us - tp->inner.cinf.reqstart_us))/US_IN_MS);
	  else
	    REP(f, "X/");

	  if (tp->inner.sinf.repend_us != 0 && tp->inner.sinf.repstart_us != 0)
	    REP(f, "%.3f ms\n", 
		((float)(tp->inner.sinf.repend_us - tp->inner.sinf.repstart_us))/US_IN_MS);
	  else
	    REP(f, "X ms\n");
	  
	  if (tp->inner.hserver.gaps_bytes)
	    REP(f, "    missing: %u/%u\n", tp->inner.hserver.gaps_bytes, 
		tp->inner.hserver.gaps_pkts);

	  if (tp->links.totchars != 0)
	    {
#ifdef WREAD
	      /* all in one buffer */
	      print_links_buf(f, tp->links.buf, tp->links.totchars, "References");
#else
	      /* in buffer chain */
	      { 
		char *start;
		char *cp;
		links_buf_t *bp = tp->links.chain;
		REP(f, "      References:-\n"); 
		while(bp != NULL)
		  {
		    cp = bp->buf;
		    start = cp;
		    while(*cp != '\0' && cp - start < bp->nchars)
		      {
			REP(f, "      \"%s\"\n", cp++);
			cp += strlen(cp) + 1;
		      }
		    bp = bp->next;
		  }
	      }
#endif /* ifdef WREAD */
	      
	      if (tp->inner.hserver.status & TRANS_LINKS_CHARS_EX)
		REP(f, "      truncated!");
	    }
	}
    }

 s_inval:
  
  REP(f, "\n");
  
  return;
}

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

#if defined FINAL_REPORT || defined REPORT || defined WREAD || defined BIN_REPORT


/*
 * TCP stuff 
 */

void 
report_tcp_close(FILE *f, unsigned int state)
{
  int full = 0;
  int closed = 0;

  
  
  if (state & TCP_FULL_CLOSE)
    {
      full++;
      closed++;
      REP(f, "full close ");
    }
  
  if (state & TCP_FORCED_CLOSE)
    {
      closed++;
      REP(f, "forced close ");
    }
  
  if (state & TCP_EFFECTIVE_CLOSE)
    {
      closed++;
      REP(f, "effective close ");
    }
  
  if (state & TCP_QUICK_CLOSE)
    {
      closed++;
      REP(f, "quick close ");
    }

  if (!full && (state & (TCP_CLI_FIN | TCP_SERV_FIN)))
    {
      REP(f, "closed ");
      if (state & TCP_CLI_FIN)
	{
	  REP(f, "(client");
	  if (state & TCP_SERV_FIN)
	    REP(f, "/server) ");
	  else 
	    REP(f, ") ");
	}
      else
	{
	  REP(f, "(server) ");
	}
    }
  
  if (state & (TCP_CLI_RST | TCP_SERV_RST))
    {
      closed++;
      REP(f, "reset ");
      if (state & TCP_CLI_RST)
	{
	  REP(f, "(client");
	  if (state & TCP_SERV_RST)
	    REP(f, "/server) ");
	  else 
	    REP(f, ") ");
	}
      else
	REP(f, "(server) ");
    }
  
  if (!closed)
    REP(f, "open ");

  if (state & TCP_TIMEO)
    {
      REP(f, "timed out ");
    }

  if (state & TCP_RUNEND)
    REP(f, "run ended ");

  if(state & (TCP_SERV_SERV_ERR | TCP_CLI_SERV_ERR))
    {
      REP(f, "error ");
      if (state & TCP_CLI_SERV_ERR)
	{
	  REP(f, "(client");
	  if (state & TCP_SERV_SERV_ERR)
	    REP(f, "/server) ");
	  else 
	    REP(f, ") ");
	}
      else
	REP(f, "(server) ");
    }

  REP(f, "\n");

  if (state & TCP_FORCED)
    {
      REP(f, "*** FORCED ");
      if (state & TCP_FORCED_OPEN)
	REP(f, "OPEN ");
      if (state & TCP_FORCED_CLOSE)
	REP(f, "CLOSE ");
      if (state & TCP_FORCED_ALT)
	REP(f, "ALT ");
      REP(f, "***\n");
    }
  
  return;
}

void 
report_syn_nonsense(FILE *f, unsigned int state)
{
  if (state & TSP_SYN_NONSENSE)
    {
      REP(f, "** SYN ");
      if (state & TSP_DUP_SYN)
	REP(f, "DUP ");
      if (state & TSP_SYN_TOO_LATE)
	REP(f, "LATE ");
      if (state & TSP_MAVERICK_SYN)
	REP(f, "MAVERICK ");
      REP(f, "** ");
    }

  if (state & TSP_CONN_FORCED)
    {
      REP(f, "** FORCED ");
      if (state & TSP_FORCED_OPEN)
	REP(f, "OPEN ");
      if (state & TSP_FORCED_CLOSE)
	REP(f, "CLOSE ");
      if (state & TSP_FORCED_ALT)
	REP(f, "ALT ");
      REP(f, "** ");
    }
  
  REP(f, "\n");

  return;
}
  

void 
report_timeo_type(FILE *f, unsigned int state)
{
  REP(f, "( ");
  if (state & TSP_SEQ_HELD)
    REP(f, "held ");
  if (state & TSP_SEQ_TIMEO)
    REP(f, "to ");
  if (state & TSP_SEQ_TIMEO_FORCED)
    REP(f, "tf ");
  if (state & TSP_SEQ_TIMEO_QL)
    REP(f, "tq ");
  if (state & TSP_SEQ_TIMEO_ACK)
    REP(f, "ta ");
  REP(f, ")\n");

  return;
}

void 
report_flags(FILE *f, unsigned int state)
{
  if (state & TSP_SYN)
    REP(f, "S");
  if (state & TSP_RST)
    REP(f, "R");
  if (state & TSP_FIN)
    REP(f, "F");

  if (state & (TSP_SYN | TSP_RST | TSP_FIN))
    REP(f, " ");
  
  return;
}

void 
report_tcp_conn(FILE *f, tcp_conn_t *tconnp, unsigned int indx, int print_hdrs) 
{

  int i;
  http_trans_t *tp = tconnp->su.http.trans;
  tcp_simplex_flow_t *client = &tconnp->tcp.client;
  tcp_simplex_flow_t *server = &tconnp->tcp.server;
  int client_seen = tconnp->flow_inner.state & TCP_CLIENT_SEEN;
  int server_seen = tconnp->flow_inner.state & TCP_SERVER_SEEN;
  unsigned int csyn = 0L, ssyn = 0L;
  unsigned int crst = 0L, srst = 0L;
  us_clock_t start = tconnp->flow_inner.first_arr_tm; 
  us_clock_t end = tconnp->flow_inner.last_arr_tm;
  us_clock_t dur = end - start;

  if (client_seen)
    {
      csyn = tconnp->tcp.client.syn_us;
      crst = tconnp->tcp.client.rst_us;
    }

  if (server_seen)
    {
      ssyn = tconnp->tcp.server.syn_us;
      srst = tconnp->tcp.server.rst_us;
    }

  REP(f, " #%u", indx);
 
  REP(f, " %s <> %s TCP #%u ", 
      get_hname((char *)&tconnp->flow_inner.srcaddr), 
      get_hname((char *)&tconnp->flow_inner.dstaddr),
      tconnp->hdrs.conn_id);

  REP(f, "(%s <> ", get_atmaddr(tconnp->flow_inner.src_atmdata));
  REP(f, "%s)\n", get_atmaddr(tconnp->flow_inner.dst_atmdata));

  REP(f, "  %s <> %s ", 
	 tcpudp_port_string(ntohs(tconnp->flow_inner.srcport), FLOW_TCP), 
	 tcpudp_port_string(ntohs(tconnp->flow_inner.dstport), FLOW_TCP));

  REP(f, "%s", tcp_pload_string(tconnp->flow_inner.serv_type));
  if (tconnp->flow_inner.serv_type == TCP_SERV_HTTP)
    {
      if (tconnp->su.http.meta.status & HTTP_WAS_PERSISTENT)
	REP(f, "-P ");
      else if (tconnp->su.http.meta.status & HTTP_CLOSE)
	REP(f, "-NP ");
      else 
	REP(f, "-U ");
    }

  report_tcp_close(f, tconnp->flow_inner.state);

  if (client_seen)
    REP(f, "clisyn %u\n", csyn);

  REP(f, "    Open %s  ", us_clock_time_string(start));
  REP(f, "close %s  ", us_clock_time_string(end));
  
  REP(f, "Duration %.3f ms\n", (float)dur/US_IN_MS);
  REP(f, "Client mss %hu Server mss %hu\n", client->mss, server->mss);
  
  //REP(f, "\n");

  if (client_seen)
    {
      REP(f, "  client: %u/%u octets/pkts. (%u empty) ", client->tot_bytes, 
	     client->tot_pkts,  client->tot_e_pkts);
      REP(f, "(seq %u ack %u) ", 
	  client->solidseq - client->firstseq, 
	  client->lastack - client->firstack);
      report_flags(f, client->state);
      report_syn_nonsense(f, client->state);
      if (client->duplicate_bytes)
	REP(f, "    retransmitted: %u/%u\n", client->duplicate_bytes,
	       client->duplicate_pkts);
      if (client->ooo_bytes)
	REP(f, "    ooo: %u/%u\n", client->ooo_bytes, client->ooo_pkts);
      if (client->gap_bytes)
	{
	  REP(f, "    sequence gaps: %u/%u ", client->gap_bytes, client->gap_pkts);
	  report_timeo_type(f, client->state);
	}
    }

  if (server_seen)
    {
      REP(f, "  server: %u/%u octets/pkts. (%u empty) ", server->tot_bytes, 
	  server->tot_pkts,  server->tot_e_pkts);
      REP(f, "(seq %u ack %u) ", 
	  server->solidseq - server->firstseq, 
	  server->lastack - server->firstack);
      report_flags(f, server->state);
      report_syn_nonsense(f, server->state);
      if (server->duplicate_bytes)
	REP(f, "    retransmitted: %u/%u\n", server->duplicate_bytes,
	    server->duplicate_pkts);
      if (server->ooo_bytes)
	REP(f, "    ooo: %u/%u\n", server->ooo_bytes, server->ooo_pkts);
      if (server->gap_bytes)
	{
	  REP(f, "    sequence gaps: %u/%u ", server->gap_bytes, server->gap_pkts);
	  report_timeo_type(f, server->state);
	}
    }

  REP(f, "\n");

  switch (tconnp->flow_inner.serv_type)
    {
    case TCP_SERV_HTTP:
  
      REP(f, "%d transactions (%d/2 dummy)\n", 
	  tconnp->su.http.meta.ntrans, tconnp->su.http.meta.ndummytrans);
      if (tconnp->su.http.trans)
	{
	  //REP(f, "%d transactions (%d/2 dummy)\n", 
	  //tconnp->su.http.meta.ntrans, tconnp->su.http.meta.ndummytrans);
	  for (i = 0, tp = tconnp->su.http.trans; 
	       i < tconnp->su.http.meta.ntrans; 
	       i++, tp = tp->next)
	    {
	      REP(f, "Trans #%d\n", i);
	      print_trans(f, tp, -1, client_seen, server_seen, 
			  tconnp->su.http.addit_trans_fields, start);
	    }
	  
	  if (!tconnp->su.http.meta.ntrans)
	    REP(f, "\n");
	}
      break;

    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:
      fprintf(stderr, "Report_tcp_conn() - unknown service type (%hu)\n",
	     tconnp->flow_inner.serv_type);
      exit (1);
      break;
    }
  /* end switch */

  if (print_hdrs)
    {
      
      REP(f, " %d %s headers\n", tconnp->hdrs.nheld, 
	  tconnp->flow_inner.state & TCP_HDRS_DUMPED ? "remaining" : "");
      
      for (i = 0; i < tconnp->hdrs.nheld; i++)
	print_hdr_rec(f, &tconnp->hdrs.hdrs[i], &tconnp->hdrs.atm);
    }
  
  REP(f, "\n");

#ifdef PRINT_OUT
  fflush(repfile);
#endif
  
  
  return;
}

/*
 * Report TCP open record 
 */

void 
report_tcp_open(tcp_open_t *flow, us_clock_t tm, unsigned int indx)
{
  REP(stdout, " #%u %s TCP open id #%u ", indx, us_clock_ts_string(tm), flow->conn_id);
  print_flow(stdout, &flow->flow, CLIENT);
  REP(stdout, "\n");

  return;
}

/*
 * Report a block of TCP hdr info 
 */
void 
report_tcp_hdrs(tcp_hdrs_t *hdrs, unsigned int indx)
{
  int i;
  us_clock_t atm = hdrs->atm;

  REP(stdout, " #%u TCP id %u %d headers\n", indx, hdrs->conn_id, hdrs->nheld);

  for (i = 0; i < hdrs->nheld; i++)
    print_hdr_rec(stdout, &hdrs->hdrs[i], &atm);

  REP(stdout, "\n");

  return;
}
  

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

/*
 * UDP stuff 
 */

void 
report_udp_conn(udp_conn_t *uconnp, unsigned int indx) 
{

  udp_simplex_flow_t *client = &uconnp->udp.client;
  udp_simplex_flow_t *server = &uconnp->udp.server;
  int client_seen = uconnp->flow_inner.state & UDP_CLIENT_SEEN;
  int server_seen = uconnp->flow_inner.state & UDP_SERVER_SEEN;
  us_clock_t cstart = 0L, sstart = 0L;
  us_clock_t cend = 0L, send = 0L;
  us_clock_t start = uconnp->flow_inner.first_arr_tm;
  us_clock_t end = uconnp->flow_inner.last_arr_tm;
  us_clock_t dur = end - start;

  if (client_seen)
    {
      cstart = uconnp->udp.client.start_us;
      cend = uconnp->udp.client.end_us;
    }

  if (server_seen)
    {
      sstart = uconnp->udp.server.start_us;
      send = uconnp->udp.server.end_us;
    }
 
  REP(stdout, " #%u %s <> %s UDP ", indx, 
      get_hname((char *)&uconnp->flow_inner.srcaddr), 
      get_hname((char *)&uconnp->flow_inner.dstaddr));

  REP(stdout, "(%s <> ", get_atmaddr(uconnp->flow_inner.src_atmdata));
  REP(stdout, "%s)\n", get_atmaddr(uconnp->flow_inner.dst_atmdata));

  REP(stdout, "  %s <> %s ", 
	 tcpudp_port_string(ntohs(uconnp->flow_inner.srcport), FLOW_UDP), 
	 tcpudp_port_string(ntohs(uconnp->flow_inner.dstport), FLOW_UDP));

  REP(stdout, "%s ", udp_pload_string(uconnp->flow_inner.serv_type));

  if(uconnp->flow_inner.state & UDP_TIMEO)
    REP(stdout, "timed out\n");
  else if (uconnp->flow_inner.state & UDP_SERV_DONE)
    REP(stdout, "done\n");
  else
    REP(stdout, "open\n");

  REP(stdout, "    Open %s  ", us_clock_ts_string(start));
  REP(stdout, "close %s  ", us_clock_ts_string(end));

  REP(stdout, "Duration %.3f ms\n", (float)dur/US_IN_MS);

  if (client_seen)
    {
      REP(stdout, "  client: %u/%u octets/pkts\n", client->tot_bytes, 
	     client->tot_pkts);
    }

  if (server_seen)
    {
      REP(stdout, "  server: %u/%u octets/pkts\n", server->tot_bytes, 
	  server->tot_pkts);
    }

  if (uconnp->service_data)
    {
      switch (uconnp->flow_inner.serv_type)
	{
	case UDP_SERV_DNS: 
	  report_ns(uconnp->service_data); break;
	case UDP_SERV_NFS:
	case UDP_SERV_ICQ:
	case UDP_SERV_OTHER:
	  break;				/* nothing */
	default:
	  break;
	}
    }

  printf("\n");
  return;
}

/* 
 * DNS stuff 
 */

/* 
 * Report ns record 
 */
void 
report_ns(ns_fullrec_t *nfp)
{
  ns_rec_t *np = &nfp->ns_rec;
  unsigned short state = np->state;
  ns_parms_t parms = np->ns_parms;
  int bad = 0;

  printf("\t");

  if (state & NS_CLIENT_SEEN)
    {
      if (state & NS_REQ_REQ)
	{
	  printf("request %s%s", ns_ops[parms.opcode], parms.rd ? " R" : "");
	}
      else
	{
	  printf("Client not request");
	  bad++;
	}
      if (state & NS_SERVER_SEEN)
	{
	  printf(" + ");
	}
      else
	{
	  printf("\n");
	}
    }

  if (state &  NS_SERVER_SEEN)
    {
      if (state & NS_RESP_RESP)
	{
	  printf("response%s%s%s %s ", 
		 parms.aa ? " A" : "", parms.tc ? " |" : "",
		 parms.ra ? " R" : "", ns_resp[parms.rcode]);
	}
      else
	{
	  printf("Server not response");
	  bad++;
	}
      printf("\n");
    }
      

  if (state & NS_UNMATCHED_RESPONSE)
    printf("\tUnmatched response\n");

  if (state & NS_RR_TRUNC)
    printf("\tRR truncated\n");

  if (bad)
    return;

  if (state & NS_RRS_FOLLOW)
    {
      char *cp = nfp->rrbuf;
      //nsbuf(np);

      if (state & NS_NONPRINT)
	printf("\tRR contains non-prints\n");

      while (*cp)
	{
	  switch (*cp++)
	    {
	    case RR_NONE:
	      break;
	    case RR_NOT_INET:
	      printf("\tRR class ");
	      printf("%s", tok2str(class2str, "(Class %d)", *((unsigned short *)cp)));
	      cp +=2;
	      printf("\n");
	      break;
	    case RR_OTHER_TYPE:
	      printf("\tRR type ");
	      printf("%s", tok2str(type2str, "(Type %d)", *((unsigned short *)cp)));
	      cp +=2;
	      printf("\n");
	      break;
	    case RR_IPADDR:
	      printf("\t");
	      cp += printf("%s", cp) + 1;
	      printf("  ->  %s\n", intoa(*((unsigned int *)cp)));
	      cp += 4;
	      break;
	    case RR_CNAME:
	      printf("\t");
	      cp += printf("%s", cp) + 1;
	      printf("  ->  ");
	      cp += printf("%s", cp) + 1;
	      printf ("\n");
	      break;
	    case RR_REQ:
	      printf("\t(");
	      cp += printf("%s", cp) + 1;
	      printf(")\n");
	      break;
	    default:
	      fprintf(stderr, "report_ns - unknown rr type %x\n", *(cp - 1));
	      exit (1);
	      break;
	    }
	}
    }
	  

  

  return;
}

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

/*
 * Report icmp record 
 */

void 
report_icmp(struct icmp_rec *ip, int indx)
{
  REP(stdout, " #%u %s ICMP ", indx, us_clock_ts_string(ip->tm));
  REP(stdout, "%s %s\n", icmp_type_string(ip->type), 
      icmp_code_string(ip->code));
  REP(stdout, "%s:%d <> ", get_hname((char *)&ip->src_addr), 
      ntohs(ip->src_port));
  REP(stdout, "%s:%d\n", get_hname((char *)&ip->dst_addr), 
      ntohs(ip->dst_port));

  return;
}

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

/*
 *  Report nic fail/drop record
 */

void 
report_nic_fail(net_device_stats_t *rec, int indx)
{
  REP(stdout, " #%u XXX %s nic %s fail\n", indx, ts_string(&rec->ts), 
      rec->if_name);
  if (rec->rx_errors)
    REP(stdout, "\t%u rx errors\n", rec->rx_errors);
  if (rec->rx_dropped)
    REP(stdout, "\t%u rx drops\n", rec->rx_dropped);
  if (rec->rx_length)
    REP(stdout, "\t%u rx length errors\n", rec->rx_length);
  if (rec->rx_over)
    REP(stdout, "\t%u rx over runs\n", rec->rx_over);
  if (rec->rx_crc)
    REP(stdout, "\t%u rx crc errors\n", rec->rx_crc);
  if (rec->rx_frame)
    REP(stdout, "\t%u rx frame errors\n", rec->rx_frame);
  if (rec->rx_fifo)
    REP(stdout, "\t%u rx fifo errors\n", rec->rx_fifo);
  if (rec->rx_missed)
    REP(stdout, "\t%u rx missed\n", rec->rx_missed);

  REP(stdout, "\n");
  

  return;
}

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

/*
 *  Report sk_buff alloc fail record
 */

void 
report_buf_alloc_fail(sk_buff_alloc_fail_rec_t *rec, int indx)
{
  REP(stdout, " #%u XXX %s sk_buff %d alloc fails\n\n", indx, ts_string(&rec->ts), 
      rec->nfails);

  return;
}

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

/* 
 * Report accounting period report 
 */

void 
report_period_report(period_report_t *prep, int indx, counters_t *ctrs, int why)
{
  
  if (why == REC_WIRE_PERIOD_REPORT)
    REP(stdout, "Wire driven:-\n");
  REP(stdout, " #%u %s\n", indx, time_string(&prep->ts));
  if (prep->buf_alloc_fails)
    REP(stdout, " XXX %u sk_buff alloc fails XXX\n", prep->buf_alloc_fails);
  if (prep->nic_errs)
    REP(stdout, " XXX %d nic fails XXX\n", prep->nic_errs);
  REP(stdout, " recvd: tous %uin-%uout=%u : frus %uin-%uout=%u\n",
      prep->tous_in, prep->tous_out, prep->tous_in - prep->tous_out,
      prep->frus_in, prep->frus_out, prep->frus_in - prep->frus_out);
  REP(stdout, " WIRE %.3f Mbs, %d pkts\n", prep->wire_mbs, prep->wire_pkts);
  REP(stdout, " TCP: %.3f Mbs, %d pkts ", prep->tcp_mbs, prep->tcp_pkts);
  REP(stdout, " UDP: %.3f Mbs, %d pkts ", prep->udp_mbs, prep->udp_pkts);
  REP(stdout, " HTTP: %.3f Mbs, %d pkts\n", prep->http_mbs, prep->http_pkts);
  REP(stdout, " Holding %u buffers lag %u us\n", prep->buff_held, 
	  prep->lag);
  REP(stdout, " Max fetch interval %u\n", prep->max_fetch_interval);
  REP(stdout, " Parsed %u bytes HTML\n", prep->html_parsed);
  REP(stdout, " report - %3.0f%% (%u) of cycle (%u records %u total)\n",  
      ((float)prep->rep_bytes_written*100)/ctrs->fh.rep_fsz,
      prep->rep_bytes_written,
      prep->nrecords, 
      prep->nrecords_tot + prep->nrecords);
  REP(stdout, " dump - %3.0f%% (%u) of cycle\n",  
      ((float)prep->dump_bytes_written*100)/ctrs->fh.dump_fsz, 
      prep->dump_bytes_written);
  REP(stdout, " dumped %u rep %u dump\n", prep->repblks_dumped*WRITE_BLKSZ,
	  prep->dumpblks_dumped*WRITE_BLKSZ);
  REP(stdout, " Timeouts: flow: tcp: %u  seq: to %u tf %u tq %u ta %u udp: %u\n", 
      prep->tcp_to, prep->tcp_seq_to, prep->tcp_seq_to_forced, 
      prep->tcp_seq_to_q, prep->tcp_seq_to_ack, 
      prep->udp_to);
  REP(stdout, " Save rate %.4f MBs\n", prep->save_mBs);
  REP(stdout, " Maximum inter arrival time (sched) %u us\n", 
      prep->max_int_get);
  REP(stdout, " Minimum forced TCP sequence timeout %u ms\n",
      prep->min_tcp_seq_tp_f);
  REP(stdout, " Running time %s\n", us_clock_ts_string(prep->running_time));
  REP(stdout, "\n");

  return;
}

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

/* 
 * Report rusage report 
 */

void 
report_rusage(np_rusage_t *ru, int indx, int show_stamp)
{
  long long udiff;
  long long usage, susage, wusage, wsusage;
  static struct timeval last_ts = {0l, 0L};
  static struct timeval last_ru_utime = {0L, 0L};
  static struct timeval last_ru_stime = {0L, 0L};
  static struct timeval w_last_ru_utime = {0L, 0L};
  static struct timeval w_last_ru_stime = {0L, 0L};
  double pct, spct, wpct, wspct;

  udiff = utvsub(&ru->ts, &last_ts);
  last_ts = ru->ts;

  usage = utvsub(&ru->ru.ru_utime, &last_ru_utime);
  last_ru_utime = ru->ru.ru_utime;

  susage = utvsub(&ru->ru.ru_stime, &last_ru_stime);
  last_ru_stime = ru->ru.ru_stime;

  wusage = utvsub(&ru->wru.ru_utime, &w_last_ru_utime);
  w_last_ru_utime = ru->wru.ru_utime;

  wsusage = utvsub(&ru->wru.ru_stime, &w_last_ru_stime);
  w_last_ru_stime = ru->wru.ru_stime;

  pct = (((double)usage)*100)/udiff;
  spct = (((double)susage)*100)/udiff;
  wpct = (((double)wusage)*100)/udiff;
  wspct = (((double)wsusage)*100)/udiff;

  if (show_stamp)
    REP(stdout, " #%u %s\n", indx, time_string(&ru->ts));
  REP(stdout, " Cycles: %.2fu + %.2fs = %.2f%% writer %.2fu + %.2fs = %.2f%%\n\n",
	  pct, spct, pct+spct, wpct, wspct, wpct+wspct);

  return;
}
  

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

/* 
 * Report something interesting record 
 */

void
report_interesting(flow_inner_t *flowp, int way, char *s, int indx)
{
  REP(stdout, " #%u Interesting (%s)\n", indx, 
      way == SERVER ? "Server" : "Client");
  print_flow(stdout, flowp, way);
  printf ("%s\n\n", s);

  return;
}

void
report_inform(char *s, int indx)
{
  REP(stdout, " #%u Inform\n", indx);
  printf ("%s\n\n", s);

  return;
}
  

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

/* 
 * Report stats from /proc 
 */

void
report_procstats(procstat_rec_t *prp, int ncpus, int hz, int pgsz, int indx)
{
  procstat_procrec_t *proc = &prp->proc;
  procstat_statrec_t *stat = &prp->stat;
  procstat_memrec_t *mem = &prp->mem;

  float per_sec, per_jiffies, per_time;
  float tot_cpu = (stat->utm + stat->stm + stat->itm);
  float chann_cpu = tot_cpu/ncpus;

  static int started = 0;
  static unsigned int  kb_per_page;
  static struct timeval last;
  

  if (!started)
    {
      started = 1;
      kb_per_page = pgsz / 1024;
      per_time = 0.0;
    }
  else 
    {
      per_time = utvsub(&(prp->ts), &last)/1000000.0;
    }

  per_jiffies = tot_cpu/ncpus;
  per_sec = per_jiffies/hz;
  
  last = *(&prp->ts);			/* struct */

  REP(stdout, " #%u %s per %.1fs (%.1fs)\n", indx, time_string(&prp->ts), 
      per_time, per_sec);

  REP(stdout, " SYSTEM\n");
  REP(stdout, " Mem: Free %u Buffer %u Cache %u Swapped %u kB\n", 
      mem->mfree>>10, mem->mbuff>>10, mem->memc>>10, mem->memswp>>10);
  REP(stdout, " CPU: usr %.0f%% sys %.0f%% idle %.0f%%\n", 
      stat->utm*100/tot_cpu, 
      stat->stm*100/tot_cpu, stat->itm*100/tot_cpu);
  REP(stdout, 
      " Swap pages: in %.0f/s out %.0f/s, IO blocks: in %.0f/s out %.0f/s\n", 
      stat->swin/per_sec, stat->swout/per_sec, 
      stat->bin/per_sec, stat->bout/per_sec);
  REP(stdout, " Interrupts: %.0f/s, Cswitches: %.0f/s\n", 
      stat->itot/per_sec, stat->csw/per_sec);

  REP(stdout, " NPROBE\n");
  //REP(stdout, " CPU%%: %.0f usr %.0f sys %.0f tot\n",
  //proc->utm/tot_cpu, proc->stm/tot_cpu, (proc->utm+proc->stm)/tot_cpu);
  REP(stdout, " CPU: usr %.0f (%.0f)%% sys %.0f (%.0f)%% tot %.0f (%.0f)%%\n",
      proc->utm*100/tot_cpu, proc->utm*100/per_jiffies, 
      proc->stm*100/tot_cpu, proc->stm*100/per_jiffies, 
      (proc->utm+proc->stm)*100/tot_cpu, 
      (proc->utm+proc->stm)*100/per_jiffies);
  REP(stdout, " Page faults: min %.0f/s maj %.0f/s , Swaps: %.0f/s\n",
      proc->minflt/per_sec, proc->majflt/per_sec, proc->nswap/per_sec);
  REP(stdout, " VM %ukB,  RSS %d pages\n", proc->vsz>>10, proc->rss);
  REP(stdout, " Mem pages: total %d rss %d\n mmapped %d text %d lib %d data %d dirty %d\n", 
	  proc->msz, proc->mrss, proc->mshare, 
	  proc->mtrs, proc->mlrs, proc->mdrs, proc->mdt);

  REP(stdout, "\n");    
  
  return;
}
  

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

/* 
 * Report 'wrapper' record 
 */

void
report_wrapper(wrapper_record_t *wrapper, int indx)
{
  REP(stdout, " #%u WRAPPER record type %d\n", indx, wrapper->type);
  switch (wrapper->type)
    {
    case WRAPPER_TIME_CALLS:
      report_call_times(&wrapper->data.call_times, indx);
      break;
    }
  

  return;
}
  

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

/* 
 * Report times record 
 */

void 
report_call_times(call_times_t *ct, int indx)
{
  REP(stdout, " #%u %s\n", indx, time_string(&ct->ts));
  REP(stdout, "\tIP %lldus usr, %lldus system\n", ct->ip.utm, ct->ip.stm);
  REP(stdout, "\tUDP %lldus usr, %lldus system\n", ct->udp.utm, ct->udp.stm);
  REP(stdout, "\tTCP %lldus usr, %lldus system\n", ct->tcp.utm, ct->tcp.stm);
  REP(stdout, "\tTCP in seq. %lldus usr, %lldus system\n", 
      ct->tcp_inseq.utm, ct->tcp_inseq.stm);
  REP(stdout, "\tTCP catchup %lldus usr, %lldus system\n", 
      ct->tcp_catchup.utm, ct->tcp_catchup.stm);
  REP(stdout, "\tTCP service %lldus usr, %lldus system\n", 
      ct->tcp_serv.utm, ct->tcp_serv.stm);
  REP(stdout, "\tHTTP %lldus usr, %lldus system\n", 
      ct->http.utm, ct->http.stm);
  REP(stdout, "\tHTML %lldus usr, %lldus system\n", 
      ct->html.utm, ct->html.stm);
  REP(stdout, "\n");
  
  return;
}

  

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

#if !defined WREAD && !defined BIN_REPORT

void 
final_report(listhdr_t *htbl)
{
  int i, j;

  for (i = 0; i < N_HOST_BUCKETS; i++)
    {
      host_flow_t *hostlistp;
      L_WALK(hostlistp, &htbl[i], hlist, host_flow_t)
	{
	  tcp_conn_t *connlistp;
	  for (j = 0; j < N_CONN_BUCKETS; j++)
	    {
	      L_WALK(connlistp, &hostlistp->tcp_conns[j], hlist, tcp_conn_t)
		{
		  report_tcp_conn(connlistp, 0);
		}
	    }
	}
    }

#ifdef TCPDUMP_FED
  report_counters(&counters, TCPDUMP_COLL, o_infnm);
# else
  report_counters(&counters, NPROBE_COLL, NULL);
#endif

  return;
}

#endif /* ifndef WREAD && BIN_REPORT */

#ifdef REPORT

void 
report_tcp_close(tcp_conn_t *tconnp)
{

  report_tcp_conn(tconnp);

  return;
}
  
  
#endif /* ifdef REPORT */

#endif /* if defined FINAL_REPORT || defined REPORT || defined WREAD */



/*
 * end report.c 
 */
