/**************************************************************************** 
** File: tcp.c
**
** Author: Mike Borella
**
** Comments: Dump TCP header information.  TCP options section lifted from 
** tcpdump.
**
** $Log: tcp.c,v $
** Revision 1.2  1998/06/12 21:01:15  mborella
** Added log tag
**
*****************************************************************************/

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "config.h"
#include "tcp.h"

/*
 * TCP options
 */

#ifndef TCPOPT_WSCALE
#define TCPOPT_WSCALE           3       /* window scale factor (rfc1072) */
#endif
#ifndef TCPOPT_ECHO
#define TCPOPT_ECHO             6       /* echo (rfc1072) */
#endif
#ifndef TCPOPT_ECHOREPLY
#define TCPOPT_ECHOREPLY        7       /* echo (rfc1072) */
#endif
#ifndef TCPOPT_TIMESTAMP
#define TCPOPT_TIMESTAMP        8       /* timestamps (rfc1323) */
#endif

extern u_char *packet_end;
extern struct arg_t *my_args;

/*----------------------------------------------------------------------------
**
** dump_tcp()
**
** Parse TCP header and dump fields
**
**----------------------------------------------------------------------------
*/

void dump_tcp(u_char *bp, int length)
{
  TCPHdr *tp;
  u_char flags;
  int hlen, total_hlen;
  u_short sport, dport, win, urp;
  u_int seq, ack;
  char *tcpport_string(u_short);
  void dump_payload(u_char *, u_int);

  /*
   * Overlay TCP header
   */

  tp = (TCPHdr *) bp;
  
  /*
   * Dump TCP header announcement
   */
  
  printf("-----------------------------------------------------------------\n");
  printf("                        TCP Header\n");
  printf("-----------------------------------------------------------------\n");
  
  if (length < sizeof(TCPHdr)) 
    {
      printf("Truncated TCP header: %d bytes\n", length);
      return;
    }

  /*
   * Grab ports and some fields
   */

  sport = ntohs(tp->th_sport);
  dport = ntohs(tp->th_dport);
  hlen = tp->th_off * 4;
  seq = ntohl(tp->th_seq);
  ack = ntohl(tp->th_ack);
  win = ntohs(tp->th_win);
  urp = ntohs(tp->th_urp);
  flags = tp->th_flags;

  /*
   * Dump TCP header info
   */

  if (!my_args->t)
    {
      printf("Source port:            %d", sport);
      if (sport < 1024) 
	printf(" (%s)\n", tcpport_string(sport));
      else 
	printf("\n");
      
      printf("Destination port:       %d", dport);
      if (dport < 1024) 
	printf(" (%s)\n", tcpport_string(dport));
      else 
	printf("\n");
      
      printf("Sequence number:        %u\n", seq);
      printf("Acknowledgement number: %u\n", ack);
      printf("Header length:          %d\n", hlen);
      printf("Reserved bits:          %d\n", tp->th_x2);
      
      printf("Flags:                  ");
      if (flags & TH_SYN) printf("SYN ");
      if (flags & TH_FIN) printf("FIN ");
      if (flags & TH_RST) printf("RST ");
      if (flags & TH_PUSH) printf("PSH ");
      if (flags & TH_ACK) printf("ACK ");
      if (flags & TH_URG) printf("URG ");
      printf("\n");
      
      printf("Window size:            %d\n", win);
      printf("Checksum:               %d\n", ntohs(tp->th_sum));
      printf("Urgent pointer:         %d\n", urp);
      printf("Options:                ");
    }


  /*
   * Check header length
   */

  if (hlen > length) 
    {
      printf("none\nBad header length\n");
      return;
    }

  /*
   * Header length and payload length bookkeeping
   */

  length -= hlen;
  total_hlen = hlen;
  
  /*
   * Handle any options.
   */
  if ((hlen -= sizeof(TCPHdr)) > 0) 
    {
      u_char *cp;
      int i, opt, len, datalen;
      
      cp = (u_char *) tp + sizeof(TCPHdr);
      while (hlen > 0) 
	{
	  /* 
	   * Check for zero length options
	   */

	  opt = *cp++;
	  if (opt == TCPOPT_EOL || opt == TCPOPT_NOP) 
	    len = 1;
	  else 
	    {
	      len = *cp++;  /* total including type, len */
	      if (len < 2 || len > hlen)
		{
		  printf("Bad option\n");
		  break;
		}
	      
	      /*
	       * account for length byte 
	       */

	      hlen --;
	      length --;
	      
	    } /* else */

	  /* 
	   * account for type byte 
	   */
	  
	  hlen --;
	  length --;

	  /*
	   * Handle the rest of the options
	   */

	  datalen = 0;
	  switch (opt) 
	    {	      
	    case TCPOPT_MAXSEG:
	      datalen = 2;
	      if (!my_args->t)
		{
		  printf("Maximum segment size = ");
		  printf("%u\n", EXTRACT_16BITS(cp));
		}
	      break;
	      
	    case TCPOPT_EOL:
	      if (!my_args->t)
		{
		  printf("End of options list\n");
		}
	      break;
	      
	    case TCPOPT_NOP:
	      if (!my_args->t)
		{
		  printf("No op\n");
		}
	      break;
	      
	    case TCPOPT_WSCALE:
	      datalen = 1;
	      if (!my_args->t)
		{
		  printf("Window scale = ");
		  printf("%u\n", *cp);
		}
	      break;

	    case TCPOPT_ECHO:
	      datalen = 4;
	      if (!my_args->t)
		{
		  printf("Echo = ");
		  printf("%u\n", EXTRACT_32BITS(cp));
		}
	      break;

	    case TCPOPT_ECHOREPLY:
	      datalen = 4;
	      if (!my_args->t)
		{
		  printf("Echo reply = ");
		  printf("%u\n", EXTRACT_32BITS(cp));
		}
	      break;
	      
	    case TCPOPT_TIMESTAMP:
	      datalen = 8;
	      if (!my_args->t)
		{
		  printf("Timestamp = ");
		  printf("%u", EXTRACT_32BITS(cp));
		  printf(" %u\n", EXTRACT_32BITS(cp + 4));
		}
	      break;

	    default:
	      datalen = len - 2;
	      if (!my_args->t)
		{
		  printf("Option %d:", opt);
		  for (i = 0; i < datalen; ++i) 
		    printf("%02x\n", cp[i]);
		}
	      break;
	    }

	  /* 
	   * Account for data printed 
	   */
           
	  cp += datalen;
	  hlen -= datalen;
        }
    }
  else
    {
      if (!my_args->t)
	printf("none\n");
    }


  /*
   * print payload if there is one
   */

  if (my_args->p && length > 0) 
    dump_payload((u_char *) bp + total_hlen, length);

  return;
  
}
