/*  -*- 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 <ctype.h>
#include <stdlib.h>
#include <sys/param.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#ifdef __alpha__
#include <sys/mbuf.h>
#endif
#include <net/route.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#undef __STDC__
#include <netinet/ip.h>

#ifdef __alpha__
#include <netinet/ip_var.h>
#endif
#include <netinet/tcp.h>
#include <netinet/if_ether.h>

#include <assert.h>

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

#include "print_util.h"

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

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

/*
 * deal with test packets
 */

unsigned char pl_seq;
int started = 0;

void 
tcp_test_open(prec_t *pp, struct tcp_conn *tconnp, int way, unsigned char flags)
{
  printf("TEST - open called\n");
  fflush(stdout);

  if (pp->len)
    {
      started++;
      pl_seq = pp->buf[0];
    }

  return;
}

void 
tcp_test_close(prec_t *pp, struct tcp_conn *tconnp, int way, unsigned char flags)
{
  printf("TEST - close called\n");
  fflush(stdout);
  return;
}

int 
tcp_test_pkt(prec_t *pp, struct tcp_conn *tconnp, int way)
{

  if (!started)
    {
      started++;
      pl_seq = pp->buf[0];
    }

  while (pp->len)
    {
      if (*pp->buf != pl_seq)
	{
	  printf("content check FAILED\n");
	  fflush(stdout);
	  return 1;
	}
      pp->buf++;
      pp->len--;
      pl_seq++;
    }

  printf("OK ");
  fflush(stdout);
	  
  return 0;
}


int 
tcp_test_sync(struct tcp_heldpkt *hpp, struct tcp_conn *tconnp, 
	      struct tcp_simplex_flow *tsp, unsigned int gap, int way, 
	      unsigned int tm)
{
  printf("TEST - sync gap %u\n", gap);
  pl_seq += gap;
  return 1;
}
  
void 
tcp_test_dump(struct tcp_conn *tconnp)
{
  int client_seen = TCP_STATE & TCP_CLIENT_SEEN;
  int server_seen = TCP_STATE & TCP_SERVER_SEEN;

  rec_dump_start();
  /* tcp part */
  tcp_dump(tconnp, client_seen, server_seen);
  rec_dump_end(REC_TCP_TEST);
  
  return;
}

void 
print_pkt_ident(unsigned int arrtm, tcp_conn_t *tconnp, int way)
{
  struct timeval tv;
  char *src, *dst;
  unsigned short sport, dport;

  tv.tv_sec = arrtm/1000000;
  tv.tv_usec = arrtm%1000000;

  if (way == CLIENT)
    {
      src = (char *)&tconnp->flow_common.inner.srcaddr;
      sport = ntohs(tconnp->flow_common.inner.srcport);
      dst = (char *)&tconnp->flow_common.inner.dstaddr;
      dport = ntohs(tconnp->flow_common.inner.dstport);
    }
  else
    {
      dst = (char *)&tconnp->flow_common.inner.srcaddr;
      dport = ntohs(tconnp->flow_common.inner.srcport);
      src = (char *)&tconnp->flow_common.inner.dstaddr;
      sport = ntohs(tconnp->flow_common.inner.dstport);
    }  

  printf("%s %s:%s > ", 
	 ts_string(&tv), get_hname(src), tcpudp_port_string(sport, FLOW_TCP));
  printf("%s:%s ", 
	 get_hname(dst), tcpudp_port_string(dport, FLOW_TCP));
  fflush(stdout);

  return;
}

void 
print_seq(unsigned int seq, int len)
{
  printf("%u:%u(%d)", seq, seq+len, len);
  fflush(stdout);
  return;
}
  

void 
test_pkt_arrive(tcp_conn_t *tconnp, int way, unsigned int seq, unsigned int ack, int len, unsigned char flags)
{
  static int first = 1;
    
  if (first)
    {
      first--;
      print_pkt_ident(tconnp->flow_inner.last_arr_tm, tconnp, way);
      printf("\n");
    }
  printf("TEST arrival ");
  print_seq(seq, len);
  print_tcp_flags(flags, stdout);
  printf("\n");
  fflush(stdout);

  return;
}

void 
test_pkt_hold(tcp_conn_t *tconnp, tcp_simplex_flow_t *tsp, tcp_heldpkt_t *hpp)
{
  tcp_heldpkt_t *memp;

  printf("TEST hold");
  //print_pkt_ident(hpp->arr_tm, tconnp, (tsp->state & TSP_CLIENT) ? CLIENT : SERVER);
  printf(" (solidseq %u) ", tsp->solidseq);
  printf(". || ");
  L_WALK(memp, &tsp->held_list, links, tcp_heldpkt_t)
    {
      int thisone = 0;
      if (memp == hpp)
	thisone++;
      if (thisone)
	printf("*");
      print_seq(memp->seq, memp->len);
      print_tcp_flags(memp->tcp_flags, stdout);
      if (thisone)
	printf("*");
      printf(" || ");
    }
  printf(".\n");
  fflush(stdout);
  return;
}

void 
test_pkt_do(struct tcp_conn *tconnp, tcp_simplex_flow_t *tsp, int way, unsigned int seq, int len, unsigned char flags)
{
  
  printf("TEST process ");
  //print_pkt_ident(0, tconnp, (tsp->state & TSP_CLIENT) ? CLIENT : SERVER);
  print_seq(seq, len);
  print_tcp_flags(flags, stdout);
  printf(" solidseq %u ", tsp->solidseq);
  fflush(stdout);

  return;
}

void 
test_pkt_do_end(struct tcp_conn *tconnp, tcp_simplex_flow_t *tsp)
{
  printf("solidseq -> %u\n", tsp->solidseq);
  if (TCP_STATE & TCP_SERV_FIN)
    printf("sfin ");
  if (TCP_STATE & TCP_CLI_FIN)
    printf("cfin ");
  if (TCP_STATE & TCP_SERV_RST)
    printf("srst ");
  if (TCP_STATE & TCP_CLI_RST)
    printf("crst ");
  if (TCP_STATE & TCP_FULL_CLOSE)
    printf("full close ");
  if (TCP_STATE & TCP_EFFECTIVE_CLOSE)
    printf("effective close ");
  if (TCP_STATE & TCP_QUICK_CLOSE)
    printf("quick close ");

  if (TCP_STATE & (TCP_SERV_FIN | TCP_CLI_FIN | TCP_SERV_RST | TCP_CLI_RST 
		   | TCP_FULL_CLOSE | TCP_EFFECTIVE_CLOSE | TCP_QUICK_CLOSE))
    printf("\n");
  fflush(stdout);

  return;
}

tcp_serv_methods_t tcp_test_serv_methods = 
{
  tcp_test_open,
  tcp_test_pkt,
  tcp_test_sync,
  tcp_test_close,
  tcp_test_dump
};

serv_control_t tcp_test_serv_control = 
  {
    {
      &tcp_test_serv_methods
    },
    {
      REC_TCP_TEST_OPEN, REC_TCP_TEST_HDRS, REC_TCP_TEST, TCP_SERV_TEST
    },
    &counters.TRAFF_TCP_TEST,
    DUMP_OPEN | DUMP_HDRS
  };
