/*****************************************************************
 *
 * netprintf.c - Print functions to display network headers and data.
 *
 * (c) Matthew B Mathis, Carnegie Mellon University, 1988, 1993
 * All rights reserved.   See COPYRIGHT.h for additional information.
 *
 * Guiding philosophy: every byte of the message is printed exactly once
 *	in hex is network order, interspersed with commentary driven by
 *	the content.
 *
 * The following arguments are common:
 *		The first two are updated as the packet is displayed.
 *	char **pkt;	Pointer to the 'cursor' used to print the packet.
 *	int *len;	Pointer to the remaining undisplayed length.
 *	int fdata;	Count of layers to format, -1 => all
 */

/* Fix Ultrix 4.3a brain damage  */
#ifdef _CFE
#undef _CFE
#endif

#include <stdio.h>
#if HAVE_STDARG_H
#include <stdarg.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>

#ifdef CRAY
#include "craytypes.h"
#else /* CRAY */
#define STRUCTALIGN(type, ptr, offset) (type)(ptr)
#define icmp32o icmp
#define SIZEOFstructip sizeof(struct ip)
#endif /* CRAY */

int numeric=1;		/* default to 1  -> suppress namelookups */

void netprinticmp (u_char **pkt, int *len, int fdata);
void fprtdata (u_char **pkt, int *len, char *comment);
void netprinttcp (u_char **pkt, int *len, int fdata);

/*****************************************************************
 *	printf a portion of a packet per a format string
 *		"%<fmt>" "printfs" args, does not count as part of the message
 *		"#<fmt> displays bytes from the message
 *	Format  "#x" becomes "%2x"
 *		"#o" becomes "%03d"
 *		"#H" recans prior 4 bytes and displays a host addr/name
 *			Uses global 'numeric' to prevent name lookups
 *	% only supports formats %s, %d 
 */
#ifdef va_start
void fprtpkt(u_char **pkt, int *len, char *fmt, ...) {
  va_list argl;
#else /* va_start */
void fprtpkt(u_char **pkt, int *len,
	     char *fmt,           			/* format string */
	     int *args          			/* arguments for % formats */
)

/* VARARGS */
{
    int **argl = &args;
#endif /* va_start */
    register char c;
    struct hostent *hp;

#ifdef va_start
    va_start(argl, fmt);
#endif /* va_start */
    while ((c = *fmt++)) {
	if (c == '#') {
	    c = *fmt++;
	    if ( ((*len) <= 0) && (c > 'a') ){
		printf("??");
		continue;
	    }
	    switch (c) {
		case 'x':
		    printf ( "%02x", *(*pkt)++);
		    (*len)--;
		    break;
		case 'o':
		    printf ( "%03d", *(*pkt)++);
		    (*len)--;
		    break;
		case 'H':
		    printf("%d.%d.%d.%d",
			(*pkt)[-4],(*pkt)[-3],(*pkt)[-2],(*pkt)[-1]);
		    if (numeric || !(hp=gethostbyaddr((*pkt)-4, 4, AF_INET)))
			break;
		    printf(" %s",hp->h_name);
		    break;
		default:
		    printf("#%c",c);
	    }
	}
	else if (c == '%'){
	    c = *fmt++;
	    switch (c) {
		case 'd':
#ifdef va_start
	            printf("%d", va_arg(argl, int));
#else
		    printf("%d", (int)*argl++);
#endif
		    break;
		case 's':
#ifdef va_start
		    printf("%s", va_arg(argl, char *));
#else
		    printf("%s", (char *) *argl++);
#endif
		    break;
		default:
		    printf("%%%c",c);
	    }
	}
	else {
	    printf("%c",c);
	}
    }
#ifdef va_start
    va_end(argl);
#endif /* va_start */
}

#ifdef UNDEF
/*****************************************************************
 * Display an arp packet
 *
 */
fprtarp(ei,ar)
struct erroritem *ei;			/* The error under examination */
register struct addres *ar;
{
    fprtpkt ( "ARP     :   HRD=#x #x   PRO=#x #x   HLN=#x   PLN=#x    OP=#x #x\n",
	    (char *) ar, 0);
    if (ei->p_off + 8 + (2 * (ar -> ar_hln + ar -> ar_pln)) <= ERRORCOPY) {

	fprtdata ( "sha     :", ar_sha (ar), ar -> ar_hln);
	fprtdata ( "spa     :", ar_spa (ar), ar -> ar_pln);
	fprtdata ( "tha     :", ar_tha (ar), ar -> ar_hln);
	fprtdata ( "tpa     :", ar_tpa (ar), ar -> ar_pln);
    }
    else {
	fprtdata("ARP data:",ar->ar_data, ERRORCOPY - 8 - ei->p_off);
    }
}
#endif /* undef */

/*****************************************************************
 *	Display an IP packet
 *
 */
void netprintip(u_char **pkt, int *len, int fdata)
{
    struct ip *ip = (struct ip *) (*pkt);
    int     optlen,xolen;
    int	    proto,onum;
    static struct optab {int olen; char *ofmt;} otab[] = {
	{ 1,"IPoption= #x (End of options)\n"},			/* 0 */
	{ 1,"IPoption= #x (No op)\n"},				/* 1 */
	{11,"IPoption= #x (Security) #x #x#x #x#x #x#x #x#x#x\n"}, /* 2 */
	{-1,"IPoption= #x (LSRR) Len=#x Point=#x"},		/* 3 */
	{-1,"IPoption= #x (Timestamp) Len=#x Point=#x Flgs=#x"}, /* 4 */
	{ 1,"IPoption= #x (Unused)"},				/* 5 */
	{ 1,"IPoption= #x (Unused)"},				/* 6 */
	{-1,"IPoption= #x (RR) Len=#x Point=#x"},		/* 7 */
	{ 4,"IPoption= #x (Stream ID) Len=#x Id=#x #x\n"},	/* 8 */
	{-1,"IPoption= #x (SSRR) Len=#x Point=#x"},		/* 9 */
	{ 1,"IPoption= #x (Unused)\n"},				/* 10 */
	{ 1,"IPoption= #x (Unused)\n"},				/* 11 */
	{ 1,"IPoption= #x (Unused)\n"},				/* 12 */
	{ 1,"IPoption= #x (Unused)\n"},				/* 13 */
	{ 1,"IPoption= #x (Unused)\n"},				/* 14 */
	{ 1,"IPoption= #x (Unused)\n"}				/* 15 */
    };

    fprtpkt(pkt, len, "IP      : V/Hl=#x Tos=#x Len=#x #x Id=#x #x Frag=#x #x\n", 0);
    fprtpkt(pkt, len, "IP      : Ttl=#x Proto=#x Hcrc=#x #x\n", 0);
    fprtpkt(pkt, len, "IP      : Sa=#x #x #x #x (#H)\n", 0);
    fprtpkt(pkt, len, "IP      : Da=#x #x #x #x (#H)\n", 0);

    optlen = ip->ip_hl * 4 - SIZEOFstructip;
    if (optlen > 0) {
	optlen = (optlen < (*len)) ? optlen : (*len);
	(*len) -= optlen;
	while (optlen>0) {
	    onum = (**pkt) & 0xF;
	    xolen = (otab[onum].olen>0) ?0 :((*pkt)[1]-3);/* extra options */
	    fprtpkt(pkt, &optlen, otab[onum].ofmt, 0);
	    if (!xolen) continue;
	    while (xolen>0) {
		fprtpkt(pkt, &optlen,"\n    Host: #x #x #x #x (#H)", 0);
		xolen -= 4;
	    }
	    printf("\n");
	}
    }

    if (!--fdata) {
      printf("IP  data: ... (%d bytes)\n", *len);
      return;
    }
    proto = (int) ip->ip_p;			/* get the protocol */
    switch (proto){
	case IPPROTO_ICMP:
	    netprinticmp(pkt, len, fdata);
	    break;
	case IPPROTO_EGP:
	    fprtdata(pkt, len, "EGP     :");
	    break;
	case IPPROTO_UDP:
	    fprtpkt(pkt, len, "UDP hdr : SPort=#x #x DPort=#x #x   Len=#x #x CkSum=#x #x\n", 0);
	    if (!--fdata) {
	      printf("UDP data: ... (%d bytes)\n", *len);
	      return;
	    }
	    fprtdata(pkt, len, "UDP data:");
	    break;
	case IPPROTO_TCP:
	    netprinttcp(pkt, len, fdata);
	    break;
	default:
	    fprtdata(pkt, len, "IP data?:");
    }
}
/*****************************************************************
 *	Display a TCP
 *
 */
void netprinttcp(u_char **pkt, int *len, int fdata)
{
  struct tcphdr *tcp = (struct tcphdr *) (*pkt);
  int     optlen, onum;
  static struct optab {int olen; char *ofmt;} otab[] = {
    { 1, "TCPopt  = #x (End of options)\n"},		/* 0 */
    { 1, "TCPopt  = #x (No op)\n"},			/* 1 */
    { 4, "TCPopt  = #x (MSS) Len=#x mss=#x #x\n"},	/* 2 */
    { 3, "TCPopt  = #x (Window Scale) Len=#x Shift=#x\n"}, /* 3 */
    { 1, "TCPopt  = #x (unused ?)\n"},			/* 4 */
    { 1, "TCPopt  = #x (unused ?)\n"},			/* 5 */
    { 1, "TCPopt  = #x (unused ?)\n"},			/* 6 */
    { 1, "TCPopt  = #x (unused ?)\n"},			/* 7 */
    { 10, "TCPopt  = #x (Timestamps) Len=#x TSval=#x #x #x #x  TSecr=#x #x #x #x\n"},			/* 8 */
    { 1, "TCPopt  = #x (unused ?)\n"},			/* 9 */
    { 1, "TCPopt  = #x (unused ?)\n"},			/* 10 */
    { 1, "TCPopt  = #x (unused ?)\n"},			/* 11 */
    { 1, "TCPopt  = #x (unused ?)\n"},			/* 12 */
    { 1, "TCPopt  = #x (unused ?)\n"},			/* 13 */
    { 1, "TCPopt  = #x (unused ?)\n"},			/* 14 */
    { 1, "TCPopt  = #x (unused ?)\n"},			/* 15 */
  };

  fprtpkt(pkt, len, "TCP     : sp=#x #x dp=#x #x   seq=#x #x #x #x   ack=#x #x #x #x\n", 0);
  fprtpkt(pkt, len, "TCP     : do=#x    cb=#x      win=#x #x   cks=#x #x    up=#x #x\n", 0);

  optlen = (tcp->th_off - 5) * 4;
  if (optlen > 0) {
    optlen = (optlen < (*len)) ? optlen : (*len);
    (*len) -= optlen;
    while (optlen>0) {
      onum = (**pkt) & 0xF;
      fprtpkt(pkt, &optlen, otab[onum].ofmt, 0);
    }
  }

  if (!--fdata) {
    printf("TCP data: ... (%d bytes)\n", *len);
    return;
  }

  fprtdata(pkt, len, "TCP rest:");
}
/*****************************************************************
 *	Display an ICMP
 *
 */
void netprinticmp(u_char **pkt, int *len, int fdata)
{
    struct icmp32o *icmp;

    int itype;
    static char *ictypes[] = {
		"Echo Reply","Undef","Undef","Dest Unreach","Source Quence",
		"Redirect","Undef","Undef","Echo","Undef","Undef",
		"Time Exceeded","Parameter Problem","Timestamp",
		"Timestamp Reply","Info Req","Info Reply", "Mask Req",
		"Mask Reply", "????"};
    icmp = STRUCTALIGN(struct icmp32o *, *pkt, 4);

    itype = icmp->icmp_type;
    if (itype<0 || itype>ICMP_MASKREPLY) itype = ICMP_MASKREPLY+1;

    fprtpkt(pkt, len, "ICMP    : Type=#x (%s) Code=#x Cksm=#x #x ",
			(int*)ictypes[itype]);
    switch (itype) {

    case ICMP_UNREACH:
    case ICMP_TIMXCEED:
    case ICMP_SOURCEQUENCH:
	fprtpkt(pkt, len, "Unused=#x #x #x #x\n", 0);
	goto err;
    case ICMP_REDIRECT:
	fprtpkt(pkt, len, "Gway=#x #x #x #x\n", 0);
	goto err;
    case ICMP_PARAMPROB:
	fprtpkt(pkt, len, "Point=#x Unused=#x #x #x\n", 0);
err:	if (!--fdata) return;
	netprintip(pkt, len, fdata);	/* note possible recursion */
	return;
    case ICMP_ECHOREPLY:
    case ICMP_ECHO:
    case ICMP_TSTAMP:
    case ICMP_TSTAMPREPLY:
    case ICMP_IREQ:
    case ICMP_IREQREPLY:
    case ICMP_MASKREQ:
    case ICMP_MASKREPLY:
	fprtpkt(pkt, len, "Id=#x #x Seq=#x #x\n", 0);
	break;
    default:
	printf("\n");
    }

    if (!--fdata) {
      printf("ICMPdata: ... (%d bytes)\n", *len);
      return;
    }

    fprtdata(pkt, len, "ICMPdata:");
}

/*****************************************************************
 *	Display undeciphered bytes in a reasonable format
 *
 */
void fprtdata(u_char **pkt, int *len,
	      char *comment        /* A name for what we are showing */
	      )
{
    int     cnt;
    for (cnt = 0; (*len)>0 ; cnt++,(*len)-- ) {
	if (((cnt) % 16) == 0) {
	    if (cnt != 0) printf ( "\n");
	    printf ( "%s ", comment);
	}
	else if (((cnt) % 8) == 0)
	    printf ( "   ");
	printf ("%02x ", *(*pkt)++);
    }
    printf ("\n");
}
/* ruler        "comment : dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd dd */
