/*  -*- 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 <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.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
#ifdef __linux__
#define __FAVOR_BSD
#endif
#include <netinet/tcp.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>
#include <netinet/ip_icmp.h>
#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <assert.h>
#include <arpa/nameser.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 "print_util.h"
#include "http_util.h"
#include "content_t.h"
#include "np_file.h"

#include "cprintf.h"

#define HTTP_PARSE_DEBUG

int print_packets = 0;
int print_hostnames = 0;

#if defined SWIG_ONLY || defined SWIG
char null_string[] = {'\0'};
#endif


#define TBUFSZ 250

/*
 * Print struct timeval
 */
void
print_ts(tmval *tvp, FILE *f)
{
  int i = tvp->tv_sec;
  fprintf(f, "%02d:%02d:%02d.%06lu ",
		 i / 3600, (i % 3600) / 60, i % 60, tvp->tv_usec);
  return;
}

/*
 * Return struct timeval as string
 */
char *
ts_string(tmval *tvp)
{
  static char tbuf[TBUFSZ];
  int i = tvp->tv_sec;
  sprintf(tbuf, "%02d:%02d:%02d.%06lu ",
		 i / 3600, (i % 3600) / 60, i % 60, tvp->tv_usec);
  return tbuf;
}


/*
 * Return nice ascii presentation of struct timeval
 */

char *
time_string(tmval *tvp)
{
  static char tbuf[TBUFSZ];
  char *tbp = tbuf;
  time_t tm = (time_t)tvp->tv_sec;
  struct tm *time = gmtime(&tm);
  //printf("time {%d, %d}, %d sec\n", tvp->tv_sec, tvp->tv_usec, tm);
  //printf("%x %x %x %x \n", tm & 0xff, tm>>8 & 0xff, tm>>16 & 0xff, tm>>24 & 0xff);
  
  tbp += strftime(tbuf, TBUFSZ, "%a %d %b %H:%M:%S", time);
  sprintf(tbp, ".%.3lu", tvp->tv_usec/1000);

  return tbuf;
}

#ifndef SWIG_ONLY
/*
 * Return us_clock_t as a string
 */

char *
us_clock_ts_string(us_clock_t t)
{
  struct timeval tv;
  tv.tv_sec = (unsigned int)(t/US_IN_S);
  tv.tv_usec = (unsigned int)(t%US_IN_S);

  return ts_string(&tv);
}
/*
 * Return us_clock_t as a nice date/time
 */

char *
us_clock_time_string(us_clock_t t)
{
  tmval tv;
  tv.tv_sec = (unsigned int)(t/US_IN_S);
  tv.tv_usec = (unsigned int)(t%US_IN_S);

  return time_string(&tv);
}
/*
 * Return unsigned int as a string
 */

char *
us_ts_string(unsigned int t)
{
  struct timeval tv;
  tv.tv_sec = (unsigned int)(t/US_IN_S);
  tv.tv_usec = (unsigned int)(t%US_IN_S);

  return ts_string(&tv);
}
#endif /* ifndef SWIG_ONLY */

/*
 * A faster replacement for inet_ntoa().
 */
char *
intoa(unsigned int addr)
{
  char *cp;
  u_int byte;
  int n;
  static char buf[sizeof(".xxx.xxx.xxx.xxx")];
  
//  NTOHL(addr);
  addr = ntohl(addr);
  cp = &buf[sizeof buf];
  *--cp = '\0';
  
  n = 4;
  do 
    {
      byte = addr & 0xff;
      *--cp = byte % 10 + '0';
      byte /= 10;
      if (byte > 0) 
	{
	  *--cp = byte % 10 + '0';
	  byte /= 10;
	  if (byte > 0)
	    *--cp = byte + '0';
	}
      *--cp = '.';
      addr >>= 8;
    } while (--n > 0);
  
  return cp + 1;
}

/*
 * hash tables for whatever-to-name translations
 */

#define HASHNAMESIZE 4096

struct hnamemem {
	u_long addr;
	char *name;
	char proto;		/* distinguish protocol for port look-ups */
	struct hnamemem *nxt;
};

struct hnamemem hnametable[HASHNAMESIZE];
struct hnamemem tporttable[HASHNAMESIZE];

#ifdef USE_HOSTNAMES
 
/*
 * "getname" is written in this atrocious way to make sure we don't
 * wait forever while trying to get hostnames from yp.
 */
#include <setjmp.h>

jmp_buf getname_env;

static void
nohostname(unused)
int unused;
{
  longjmp(getname_env, 1);
} 

#endif

/*
 * Return a name for the IP address pointed to by ap.  This address
 * is assumed to be in network byte order.
 *
 * S'be already aligned
 */
char *
get_hname(char *ap)
{
  struct hnamemem *p;
  char *cp;
  unsigned int addr = *(unsigned int *)ap;
 
  p = &hnametable[addr & (HASHNAMESIZE-1)]; 
  for (; p->nxt; p = p->nxt) 
    {
      if (p->addr == addr)
	return (p->name);
    }

  p->addr = addr;
  p->nxt = (struct hnamemem *)calloc(1, sizeof (*p));

#ifdef  USE_HOSTNAMES
  if (print_hostnames)
    {
      if (!setjmp(getname_env)) 
	{
	  struct hostent *hp;
	  (void)signal(SIGALRM, nohostname);
	  (void)alarm(20);
	  hp = gethostbyaddr((char *)&addr, 4, AF_INET);
	  (void)alarm(0);
	  if (hp) 
	    {	
	      u_int len = strlen(hp->h_name) + 1;
	      p->name = (char *)malloc(len);
	      (void)strcpy(p->name, hp->h_name);
	      return (p->name);
	    }
	}
    }

#endif

  cp = intoa(addr);
  p->name = (char *)malloc((unsigned)(strlen(cp) + 1));
  (void)strcpy(p->name, cp);
  return (p->name);
} 

void 
print_tcp_flags(unsigned char flags, FILE *f)
{
  if (flags & TH_SYN)
    fputc('S', f);
  if (flags & TH_ACK)
    fputc('A', f);
  if (flags & TH_FIN)
    fputc('F', f);
  if (flags & TH_RST)
    fputc('R', f);
  if (flags & TH_PUSH)
    fputc('P', f);
}

#ifndef SWIG

void 
print_tcpopts(char *cp, int len, FILE *f)
{
  int i;
  char ch = '<';
  
  fputc(' ', f);
  while (--len >= 0) 
    {
      fputc(ch, f);
      switch (*cp++) 
	{
	case TCPOPT_MAXSEG:
	  {
	    unsigned short mss;
	    bcopy((char *)cp + 1, (char *)&mss, sizeof(mss));
	    (void)fprintf(f, "mss %d", ntohs(mss));
	    if (*cp != 4)
	      (void)fprintf(f, "[len %d]", *cp);
	    cp += 3;
	    len -= 3;
	    break;
	  }
	case TCPOPT_EOL:
	  (void)fprintf(f, "eol");
	  break;
	case TCPOPT_NOP:
	  (void)fprintf(f, "nop");
	  break;
	case TCPOPT_WSCALE:
	  (void)fprintf(f, "wscale %d", cp[1]);
	  if (*cp != 3)
	    (void)fprintf(f, "[len %d]", *cp);
	  cp += 2;
	  len -= 2;
	  break;
	case TCPOPT_SACKOK:
	  (void)fprintf(f, "sackOK");
	  if (*cp != 2)
	    (void)fprintf(f, "[len %d]", *cp);
	  cp += 1;
	  len -= 1;
	  break;
	case TCPOPT_ECHO:
	  {
	    unsigned int v;
	    bcopy((char *)cp + 1, (char *)&v, sizeof(v));
	    (void)fprintf(f, "echo %u", v);
	    if (*cp != 6)
	      (void)fprintf(f, "[len %d]", *cp);
	    cp += 5;
	    len -= 5;
	    break;
	  }
	case TCPOPT_ECHOREPLY:
	  {
	    unsigned int v;
	    bcopy((char *)cp + 1, (char *)&v, sizeof(v));
	    (void)fprintf(f, "echoreply %u", v);
	    if (*cp != 6)
	      (void)fprintf(f, "[len %d]", *cp);
	    cp += 5;
	    len -= 5;
	    break;
	  }
	default:
	  (void)fprintf(f, "opt-%d:", cp[-1]);
	  for (i = *cp++ - 2, len -= i + 1; i > 0; --i)
	    (void)fprintf(f, "%02x", *cp++);
	  break;
	}
      ch = ',';
    }
  fputc('>', f);
  
  return;
}

#endif /* ifndef SWIG */

/* 
 * Return textual version of port number (port in host order)
 */

#define TCP 0
#define UDP 1

char *
tcpudp_port_string(unsigned short port, char proto)
{
  struct hnamemem *tp;
  struct servent *sep;
  int i = port;
  char *prot;
  
  /* got it already? */
  for (tp = &tporttable[i & (HASHNAMESIZE-1)]; tp->nxt; tp = tp->nxt)
    if (tp->addr == i && tp->proto == proto)
      goto done;
  
  /* else new entry */
  //tp->name = (char *)malloc(strlen("0000000000"));
  tp->name = (char *)malloc(1024);
  tp->addr = i;
  tp->proto = proto;
  tp->nxt = (struct hnamemem *)calloc(1, sizeof (*tp));

  /* cream off particular known not in, or at variance with, /etc/services */
  switch (port)
    {
    case 161: strcpy(tp->name, "SNMP"); goto done; break;
    case 8008: strcpy(tp->name, "http_alt1"); goto done; break; 
    case 8080: strcpy(tp->name, "http_alt2"); goto done; break; 
    case 8554:
    case 554: strcpy(tp->name, "RTSP"); goto done; break;
    case 4000: strcpy(tp->name, "ICQ"); goto done; break;
    default: break;
    } /* end switch */

  prot = (proto == FLOW_TCP ? "tcp" : "udp");
  /* refer to /etc/services only for wk numbers */
  if (port < 1024)
    {
      sep = getservbyport(htons(port), prot);
      endservent();
      if (sep)
	{
	  strcpy(tp->name, sep->s_name);
	   goto done;
	}
    }

  sprintf(tp->name, "%d", i);
  
 done:
  return (tp->name);
}

char *
get_atmaddr(unsigned int atmdata)
{
  static char addr[32];
  unsigned char card = atmdata >> 24;
  unsigned char vpi = (atmdata >> 16) & 0xff;
  unsigned short vci = atmdata & 0xffff;

  sprintf(addr, "%hu.%hu.%hu", 
	  card, vpi, vci);

  return addr;
}

void 
print_flow(FILE *f, flow_inner_t *tc, int way)
{
  unsigned int srcaddr, dstaddr;
  unsigned short srcport, dstport;
  unsigned int srcatmdata, dstatmdata;

  if (way == CLIENT)
    {
      srcaddr = (unsigned int)tc->srcaddr;
      dstaddr = (unsigned int)tc->dstaddr;
      srcport = ntohs(tc->srcport);
      dstport = ntohs(tc->dstport);
      srcatmdata = tc->src_atmdata;
      dstatmdata = tc->dst_atmdata;
    }
  else
    {
      srcaddr = (unsigned int)tc->dstaddr;
      dstaddr = (unsigned int)tc->srcaddr;
      srcport = ntohs(tc->dstport);
      dstport = ntohs(tc->srcport);
      srcatmdata = tc->dst_atmdata;
      dstatmdata = tc->src_atmdata;
    }

  fprintf(f, "%s:%hu > ", intoa(srcaddr), srcport);
  fprintf(f, "%s:%hu ", intoa(dstaddr), dstport);
  fprintf(f, "(%s > ", get_atmaddr(srcatmdata));
  fprintf(f, "%s)\n", get_atmaddr(dstatmdata));

  return;
}

/*
 * Return string rep. of file record type
 */

char *
np_rectype_str(unsigned char type)
{
  static char buf[128];
  char *c = buf;
  char *c1;
  
  c += sprintf(c, "REC_");
  if (type >= REC_TCP_MIN && type < REC_TCP_MAX)
    c += sprintf(c, "TCP_");
  else if (type >= REC_UDP_MIN && type < REC_UDP_MAX)
    c += sprintf(c, "UDP_");
  else
    c += sprintf(c, "?_");

  switch (type & 0xFC)
    {
    case REC_TCP_ALL: c1 = "ALL"; break;
    case REC_TCP_HTTP: c1 = "HTTP"; break;
    case REC_TCP_FTP: c1 = "FTP"; break;
    default: c1 = "?"; break;
    }
  c += sprintf(c, "%s", c1);

  if (type & OPEN_BIT)
    c += sprintf(c, "OPEN");
  if (type & OPEN_BIT)
    c += sprintf(c, "HDRS");

  return buf;
}



/*
 * Print TCP hdr record 
 */

void 
print_hdr_rec(FILE *f, struct tcp_dumphdr *hdr, us_clock_t *atm)
{
  us_clock_t abstm = 0LL;
  char *col;
  assert (hdr->way == SERVER || hdr->way == CLIENT);

  if (hdr->way == SERVER)
    col = csf_w;
  else 
    col = csf_g;

  if (atm)
    abstm = *atm;
  
  cfprintf(f, col, "  %s %c %12lu + %4hu ", us_clock_ts_string(abstm + hdr->rtm), 
	  hdr->way == CLIENT ? '>' : '<',
	  (unsigned long)hdr->seq,
	  hdr->len);
  if (hdr->flags & TH_ACK)
    cfprintf(f, col, "ack %12lu ", (unsigned long)hdr->ack);

  cfprintf(f, col, "w:%4hu ", hdr->window);
  print_tcp_flags(hdr->flags, f);
  fprintf(f, "\n");

  return;
}



char *
method_string(unsigned char meth)
{
  switch (meth)
    {
    case HTTP_METHOD_GET: return "GET"; break;
    case HTTP_METHOD_HEAD: return "HEAD"; break;
    case HTTP_METHOD_POST: return "POST"; break;
    case HTTP_METHOD_OPTIONS: return "OPTIONS"; break;
    case HTTP_METHOD_PUT: return "PUT"; break;
    case HTTP_METHOD_DELETE: return "DELETE"; break;
    case HTTP_METHOD_TRACE: return "TRACE"; break;
    case HTTP_METHOD_CONNECT: return "CONNECT"; break;
    case HTTP_METHOD_MOVE: return "MOVE"; break;
    case HTTP_METHOD_BMOVE: return "BMOVE"; break;
    case HTTP_METHOD_PROPFIND: return "PROPFIND"; break;
    case HTTP_METHOD_PROPPATCH: return "PROPPATCH"; break;
    case HTTP_METHOD_UNRECOGNISED: return "*UNRECOGNISED*"; break;
    case  HTTP_METHOD_UNKNOWN:
      /* DROP THROUGH */
    default: return "unknown"; break;
    }
}

char *
status_string(short code)
{
  if (code == HTTP_SERVER_RETCODE_UNKNOWN) 
    return "unknown";

  switch (code/100)
    {
    case 1:
      switch (code % 100)
	{
	case 0: return "continue"; break;
	case 1: return "switching protocols"; break;
	default:return "unknown informational code"; break;
	}
      break;
    case 2:
      switch (code % 100)
	{
	case 0: return "OK"; break;
	case 1: return "created"; break;
	case 2: return "accepted"; break;
	case 3: return "non_authorititive info"; break;
	case 4: return "no content"; break;
	case 5: return "reset content"; break;
	case 6: return "partial content"; break;
	default: return "unknown success code"; break;
	}
      break;
    case 3:
      switch (code % 100)
	{
	case 0: return "multiple choices"; break;
	case 1: return "moved permanently"; break;
	case 2: return "moved temporarily"; break;
	case 3: return "see other"; break;
	case 4: return "not modified"; break;
	case 5: return "use proxy"; break;
	default: return "unknown redirection code"; break;
	}
      break;
    case 4:
      switch (code % 100)
	{
	case 0: return "bad request"; break;
	case 1: return "unauthorised"; break;
	case 2: return "payment required"; break;
	case 3: return "forbidden"; break;
	case 4: return "not found"; break;
	case 5: return "method not allowed"; break;
	case 6: return "not acceptable"; break;
	case 7: return "proxy authority required"; break;
	case 8: return "request timeout"; break;
	case 9: return "conflict"; break;
	case 10: return "gone"; break;
	case 11: return "length required"; break;
	case 12: return "precondition failed"; break;
	case 13: return "request entity too large"; break;
	case 14: return "request URI too long"; break;
	case 15: return "unsupported media type"; break;
	default: return "unknown client error code"; break;
	}
      break;
    case 5:
      switch (code % 100)
	{
	case 0: return "Internal server error"; break;
	case 1: return "not implemented"; break;
	case 2: return "bad gateway"; break;
	case 3: return "service unavailable"; break;
	case 4: return "gateway timeout"; break;
	case 5: return "HTTP version not supported"; break;
	default: return "unknown server error code"; break;
	}
      break;
    default:
      return "XXXXX unknown status code"; break;
    }
}

void 
print_http_versions(unsigned int vers, FILE *f)
{
  fprintf(f, "c v");
  if (vers & HTTP_VERS_1_0)
    fprintf(f, "1.0 ");
  else if (vers & HTTP_VERS_1_1)
    fprintf(f, "1.1 ");
  else if (vers & HTTP_VERS_0_9)
    fprintf(f, "0.9 ");
  else
    fprintf(f, "? ");
  fprintf(f, "s v");
  if (vers & (HTTP_VERS_1_0 * 8))
    fprintf(f, "1.0 ");
  else if (vers & (HTTP_VERS_1_1 * 8))
    fprintf(f, "1.1 ");
  else if (vers & (HTTP_VERS_0_9 * 8))
    fprintf(f, "0.9 ");
  else
    fprintf(f, "? ");

  return;
}

char * 
http_versions_string(unsigned int vers)
{
  static char str[10];
  char *b = str;

  if (vers & HTTP_VERS_1_0)
    b += sprintf(b, "1.0");
  else if (vers & HTTP_VERS_1_1)
    b += sprintf(b, "1.1");
  else if (vers & HTTP_VERS_0_9)
    b += sprintf(b, "0.9");
  else
    b += sprintf(b, "?");
  b += sprintf(b, "/");
  if (vers & (HTTP_VERS_1_0 * 8))
    b += sprintf(b, "1.0");
  else if (vers & (HTTP_VERS_1_1 * 8))
    b += sprintf(b, "1.1");
  else if (vers & (HTTP_VERS_0_9 * 8))
    b += sprintf(b, "0.9");
  else
    b += sprintf(b, "?");

  return str;
}

char *tcp_pload_string(unsigned char plt)
{
  switch (plt)
    {
    case TCP_SERV_HTTP: return "HTTP"; break;
    case TCP_SERV_FTP: return "FTP"; break;
    case TCP_SERV_FTP_DATA: return "FTP data "; break;
    case TCP_SERV_OTHER: return "Other "; break;
    case TCP_SERV_TEST: return "Test "; break;
    case TCP_SERV_TELNET: return "Telnet "; break;
    case TCP_SERV_SMTP: return "SMTP "; break;
    case TCP_SERV_POP3: return "POP3 "; break;
    case TCP_SERV_NNTP: return "NNTP "; break;
    case TCP_SERV_NETBIOS_SSN: return "Netbios ssn "; break;
    case TCP_SERV_RTSP: return "RTSP "; break;
    case TCP_SERV_PNM: return "PNM "; break;
    default: return "ERROR TCP payload type unknown"; break;
    }

  /* NOT REACHED */
  return NULL;
}

char *udp_pload_string(unsigned char plt)
{
  switch (plt)
    {
    case UDP_SERV_NFS: return "NFS"; break;
    case UDP_SERV_DNS: return "DNS"; break;
    case UDP_SERV_OTHER: return "Other"; break;
    default: return "ERROR UDP payload type unknown"; break;
    }

  /* NOT REACHED */
  return NULL;
}

/*
 * Print character sequence as a string, checking for non-printables
 * Return No. non-printing characters
 */
int 
print_seq2str(FILE *f, char *seq, unsigned char len)
{
  char *cp = seq, *end = cp + len;
  char buff[(6*len) + 3];
  char *bufp = buff;
  int nonprints = 0;

  //putchar('\"');
  *bufp++ = '\"';
  while (cp < end)
    {
      if (isprint(*cp))
	{
	  //putchar(*cp);
	  *bufp++ = *cp;
	}
      else
	{
	  //printf("[%hd]", *cp & 0xff);
	  bufp += sprintf(bufp, "[%hd]", *cp & 0xff);
	  nonprints++;
	}
      cp++;
    }
  //putchar('\"');
  *bufp++ = '\"';
  *bufp = '\0';
  fprintf(f, "%s", buff);
  
  return nonprints;
}

#ifdef WREAD

/*
 * Return string for link record type 
 */
char *
link_rec_type_string(unsigned short type)
{
  switch (type)
    {
    case LR_INLINE: return "Inline"; break;
    case LR_LINK: return "Link"; break;
    case LR_UNKNOWN: return "?"; break;
    case LR_SCRIPTED_INLINE: return "Scr. Inline"; break;
    case LR_SCRIPTED_LINK: return "Sr. Link"; break;
    case LR_SCRIPTED_UNKNOWN: return "Scr. ?"; break;
    case LR_REL_BASE: return "Base"; break;
    case LR_END_BASE: return "End base"; break;
    case LR_TS: return "Timestamp"; break;
    case LR_REL_SCRIPT_BASE: return "Code base"; break;
    case LR_END_SCRIPT_BASE: return "End code base"; break;
    case LR_SCRIPT_ARCHIVE: return "Archive"; break;
    case LR_REDIRECT_INLINE: return "Redirect auto"; break;
    case LR_REDIRECT_LINK: return "Redirect link"; break;
    case LR_REFRESH_SELF: return "Refresh self"; break;
    case LR_REFRESH_URL: return "Refresh url"; break;
    default: return "Unknown code"; break;
    }

  return "";
}


/*
 * Print out in-line image references and time stamps 
 */
#define URL_LINE_LEN 60
void 
print_links_buf(FILE *f, unsigned char *buf, int buflen, char *label)
{
  int line_len = 0;
  unsigned short ltype;
  long per;
  unsigned char *cp = buf;
  unsigned char *end = buf + buflen;
  int ts;
#if 0
  int nrefs = _http_get_nurls(buf, buflen);
  int ndistrefs = _http_get_ndist_urls(buf, buflen);

  fprintf(f, "      %s (%d/%d):-\n        \"", label, ndistrefs, nrefs);
#endif

  fprintf(f, "  %s", label);

  while (cp < end)
    {
      if (*cp == LR_HIGH)
	{
	  cp++;
	  ltype = *cp << 8;
	}
      else
	{
	  ltype = (unsigned short)*cp;
	}
      
	  
      fprintf(f, "\n%14s  ", link_rec_type_string(ltype));
      switch (ltype)
	{
	case LR_REFRESH_URL:
	case LR_REFRESH_SELF:
	  per = *((int *)(cp+1));
	  cp += sizeof(int);
	  fprintf(f, "%lds ", per);
	  break;
	default:
	  break;
	}
      
      switch (ltype)
	{
	case LR_INLINE:
	case LR_LINK:
	case LR_UNKNOWN:
	case LR_SCRIPTED_INLINE:
	case LR_SCRIPTED_LINK:
	case LR_SCRIPTED_UNKNOWN:
	case LR_REL_BASE:
	case LR_REL_SCRIPT_BASE:
	case LR_SCRIPT_ARCHIVE:
	case LR_REDIRECT_INLINE:
	case  LR_REDIRECT_LINK:
	case LR_REFRESH_URL:
	  line_len = 0;
	  while (*(++cp) != '\0')
	    {
	      if (isprint(*cp))
		putc(*cp, f);
	      else
		fprintf(f, "[%d]", *cp);
	      if (line_len++ > URL_LINE_LEN)
		{
		  fprintf(f, "\n%14s  ", "");
		  line_len = 0;
		}
	    }
	  cp++;
	  break;
	case LR_TS:
	  ts = *((int *)++cp);
	  fprintf(f, "%7d", ts/1000);
	  fprintf(f, ". %03u ms", abs(ts%1000));
	  cp += sizeof(uint);
	  break;
	case LR_END_BASE:
	case LR_END_SCRIPT_BASE:
	  cp++;
	  break;
	default:
	  cp++;
	  break;
	} /* end switch */
    } /* end while */

  return;
}

void 
print_discrete_links_buf(struct links_chars *chars, char *title)
{
  int indx = 0;
  struct linkrec lp;

  printf("      %s:-\n", title);

  while (indx < chars->totchars)
    {
      indx = _get_next_link_rec(&lp, indx, chars);
      printf("\t%30s \t%u\n", lp.url, lp.reltm/1000);
    }

  return;
}

char *
icmp_type_string(unsigned char type)
{
  switch (type)
    {
      /* add as needed */
    case ICMP_UNREACH: return "destination unreachable"; break;
    default: return "other ICMP type"; break;
    }
}

char *
icmp_code_string(unsigned char code)
{
  switch (code)
    {
      /* add as needed */
    case ICMP_UNREACH_NET: return "network unreachable"; break;
    case ICMP_UNREACH_HOST: return "host unreachable"; break;
    case ICMP_UNREACH_PROTOCOL: return "protocol unreachable"; break;
    case ICMP_UNREACH_PORT: return "port unreachable"; break;
    case ICMP_UNREACH_NEEDFRAG: return "fragmentation"; break;
    case ICMP_UNREACH_SRCFAIL: return "src route failed"; break;
    case ICMP_UNREACH_NET_UNKNOWN: return "unknown network"; break;
    case ICMP_UNREACH_HOST_UNKNOWN: return "unknown host"; break;
    case ICMP_UNREACH_ISOLATED: return "src host isolated"; break;
    case ICMP_UNREACH_NET_PROHIB: return "network denied"; break;
    case ICMP_UNREACH_HOST_PROHIB: return "host denied"; break;
    case ICMP_UNREACH_TOSNET: return "bad tos for net"; break;
    case ICMP_UNREACH_TOSHOST: return "bad tos for host"; break;
    case ICMP_UNREACH_FILTER_PROHIB: return "admin prohibited"; break;
    case ICMP_UNREACH_HOST_PRECEDENCE: return "host precedence violation"; break;
    case ICMP_UNREACH_PRECEDENCE_CUTOFF: return "precedence cutoff"; break;
    default: return "unknown code"; break;
    }
}
	  
#endif /* ifdef WREAD */
  

#define LINE_LEN 64
    
#ifdef PRINT_OUT

void 
print_content(const unsigned char *start, const unsigned char *end, FILE *f)
{
  int i = 0;
  int zflag = 2;
  unsigned char *cp = (unsigned char *)start;

  while(cp < end)
    {
      if (i >= LINE_LEN)
	{
	  fprintf(f, "\n\t");
	  i = 0;
	}
      if (isprint(*cp))
	{
	  if (zflag > 1)
	    fprintf(f, "%s%c%s", csf_bld, *cp, csb_def);
	  else
	    fprintf(f, "%c", *cp);
	  i++;
	}
      else if (*cp == 0x0a)
	{
	  fprintf(f, "<NL>\n\t"); /* explicitly show nl */
	  i = 0;
	}
      else if (*cp == 0x0d)
	{
	  fprintf(f, "<CR>"); /* explicitly show cr */
	  i += 4;
	}
      else if (*cp == 0x09)
	{
	  fprintf(f, "<tab>"); /* explicitly show tab */
	  i += 5;
	}
      else
	{
	  fprintf(f, "[%02x]", *cp);
	  i += 4;
	}
      cp++;
    }
}

#ifdef TCPDUMP_FED
void 
print_http(char *start, int len, int trunc, tcp_conn_t *tconnp, FILE *f, int way)
#else
print_http(char *start, int len, tcp_conn_t *tconnp, FILE *f, int way)
#endif

{

  int trunc_by = 0;
  char http_delim[] = {0x0d, 0x0a, 0x0d, 0x0a, '\0'};
  u_char *http_end = (u_char *)seqstr_l(start, http_delim, len);

#ifdef TCPDUMP_FED
  trunc_by = trunc;
#endif

#ifdef HTTP_PARSE_DEBUG

  if (HTTP_STATUS & HTTP_GOT_REQ_HDR)
    {
      fprintf(f, "+req hdr: method %s ", method_string(HTTP_METH));
      if (strlen(HTTP_REQSTR))
	fprintf(f, "%s\n", HTTP_REQSTR);
    }
  if (HTTP_STATUS & HTTP_GOT_REP_HDR)
    {
      fprintf(f, "+rep hdr ");
      fprintf(f, "content type %s ", content_type_string(HTTP_REP_CONTENT_T));
      if (HTTP_REP_BODY_LEN >= 0)
	fprintf(f, "len %d ", HTTP_REP_BODY_LEN);
      else
	fprintf(f, "len ? ");
    }

  print_http_versions(HTTP_VERSIONS, f);
  fprintf(f, "connection %s ", 
	 HTTP_STATUS & HTTP_KEEP_ALIVE ? "Keep alive" 
	 : HTTP_STATUS & HTTP_CLOSE ? "Close" : "Unknown");
  fprintf(f, "\n");

#endif

  fprintf(f, "\n\t");

  if (http_end)			/* header */
    {
      http_end += 4;		/* print delim seq as part of hdr */
      print_content(start, http_end, f); /* HTTP header */
      len -= (http_end - (u_char *)start);
      start = http_end;
    }

  if (way == SERVER && len + trunc_by > 0)
    {
      if (HTTP_REP_CONTENT_T & CT_TEXT_HTML) 
	print_content(start, start + len, f);
      else
	fprintf(f, "[%d octets of content type %s]", len + trunc_by, 
	       content_type_string(HTTP_REP_CONTENT_T));
    }

#ifdef TCPDUMP_FED
  if (trunc)
    fprintf(f, "[truncated by %d octets]\n", trunc);
#endif
      
  fprintf(f, "\n");
  fflush(f);

}

#endif /* ifdef PRINT_OUT */

/* Aid for debugging */
void 
print_content(prec_t *pp)
{
  int i = 0;
  char *cp = pp->buf;
  char *end = pp->buf + pp->len;

  printf("\t");

  while(cp < end)
    {
      if (i >= LINE_LEN)
	{
	  printf("\n\t");
	  i = 0;
	}
      if (isprint(*cp))
	{
	  printf("%c", *cp);
	  i++;
	}
      else if (*cp == 0x0a)
	{
	  printf("<NL>\n\t"); /* explicitly show nl */
	  i = 0;
	}
      else if (*cp == 0x0d)
	{
	  printf("<CR>"); /* explicitly show cr */
	  i += 4;
	}
      else if (*cp == 0x09)
	{
	  printf("<tab>"); /* explicitly show tab */
	  i += 5;
	}
      else
	{
	  printf("[%02x]", *cp);
	  i += 4;
	}
      cp++;
    }

  return;
}

/*
 * DNS stuff 
 */

/* Compatibility */
#ifndef T_TXT
#define T_TXT           16              /* text strings */
#endif
#ifndef T_RP
#define T_RP            17              /* responsible person */
#endif
#ifndef T_AFSDB
#define T_AFSDB         18              /* AFS cell database */
#endif
#ifndef T_X25
#define T_X25           19              /* X_25 calling address */
#endif
#ifndef T_ISDN
#define T_ISDN          20              /* ISDN calling address */
#endif
#ifndef T_RT
#define T_RT            21              /* router */
#endif
#ifndef T_NSAP
#define T_NSAP          22              /* NSAP address */
#endif
#ifndef T_NSAP_PTR
#define T_NSAP_PTR      23              /* reverse NSAP lookup (deprecated) */
#endif
#ifndef T_SIG
#define T_SIG           24              /* security signature */
#endif
#ifndef T_KEY
#define T_KEY           25              /* security key */
#endif
#ifndef T_PX
#define T_PX            26              /* X.400 mail mapping */
#endif
#ifndef T_GPOS
#define T_GPOS          27              /* geographical position (withdrawn) */
#endif
#ifndef T_AAAA
#define T_AAAA          28              /* IP6 Address */
#endif
#ifndef T_LOC
#define T_LOC           29              /* Location Information */
#endif

#ifndef T_UNSPEC
#define T_UNSPEC        103             /* Unspecified format (binary data) */
#endif
#ifndef T_UNSPECA
#define T_UNSPECA       104             /* "unspecified ascii". Ugly MIT hack */
#endif

#ifndef C_CHAOS
#define C_CHAOS         3               /* for chaos net (MIT) */
#endif
#ifndef C_HS
#define C_HS            4               /* for Hesiod name server (MIT) (XXX) */
#endif


char *ns_ops[] = {
	"q", " inv_q", " stat", " op3", " notify", " op5", " op6", " op7",
	" op8", " updataA", " updateD", " updateDA",
	" updateM", " updateMA", " zoneInit", " zoneRef",
};

char *ns_resp[] = {
	"", " FormErr", " ServFail", " NXDomain",
	" NotImp", " Refused", " Resp6", " Resp7",
	" Resp8", " Resp9", " Resp10", " Resp11",
	" Resp12", " Resp13", " Resp14", " NoChange",
};

struct tok class2str[] = {
        { C_IN,         "IN" },         /* Not used */
        { C_CHAOS,      "CHAOS)" },
        { C_HS,         "HS" },
        { C_ANY,        "ANY" },
        { 0,            NULL }
};

struct tok type2str[] = {
        { T_A,          "A" },
        { T_NS,         "NS" },
        { T_MD,         "MD" },
        { T_MF,         "MF" },
        { T_CNAME,      "CNAME" },
        { T_SOA,        "SOA" },
        { T_MB,         "MB" },
        { T_MG,         "MG" },
        { T_MR,         "MR" },
        { T_NULL,       "NULL" },
        { T_WKS,        "WKS" },
        { T_PTR,        "PTR" },
        { T_HINFO,      "HINFO" },
        { T_MINFO,      "MINFO" },
        { T_MX,         "MX" },
        { T_TXT,        "TXT" },
        { T_RP,         "RP" },
        { T_AFSDB,      "AFSDB" },
        { T_X25,        "X25" },
        { T_ISDN,       "ISDN" },
        { T_RT,         "RT" },
        { T_NSAP,       "NSAP" },
        { T_NSAP_PTR,   "NSAP_PTR" },
        { T_SIG,        "SIG" },
        { T_KEY,        "KEY" },
        { T_PX,         "PX" },
        { T_GPOS,       "GPOS" },
        { T_AAAA,       "AAAA" },
        { T_LOC ,       "LOC " },
#ifndef T_UINFO
#define T_UINFO 100
#endif
        { T_UINFO,      "UINFO" },
#ifndef T_UID
#define T_UID 101
#endif
        { T_UID,        "UID" },
#ifndef T_GID
#define T_GID 102
#endif
        { T_GID,        "GID" },
        { T_UNSPEC,     "UNSPEC" },
        { T_UNSPECA,    "UNSPECA" },
        { T_AXFR,       "AXFR" },
        { T_MAILB,      "MAILB" },
        { T_MAILA,      "MAILA" },
        { T_ANY,        "ANY" },
        { 0,            NULL }
};


/*
 * For debugging 
 */
#define LINE_LEN 64
void
nsbuf(ns_fullrec_t *nfp)
{
  int i = 0;
  unsigned char *cp = nfp->rrbuf;
  unsigned char *end = cp + nfp->ns_rec.buflen;

  printf("\nBuff contents\n");

  while(cp < end)
    {
      if (i >= LINE_LEN)
	{
	  printf("\n\t");
	  i = 0;
	}
      if (isprint(*cp))
	{
	  printf("%c", *cp);
	  i++;
	}
      else if (*cp == 0x0a)
	{
	  printf("<NL>\n\t"); /* explicitly show nl */
	  i = 0;
	}
      else if (*cp == 0x0d)
	{
	  printf("<CR>"); /* explicitly show cr */
	  i += 4;
	}
      else if (*cp == 0x09)
	{
	  printf("<tab>"); /* explicitly show tab */
	  i += 5;
	}
      else
	{
	  printf("[%02x]", *cp);
	  i += 4;
	}
      cp++;
    }
  printf("\n");
  fflush(stdout);
}

/*
 * Convert a token value to a string; use "fmt" if not found.
 */
const char *
tok2str(struct tok *lp, char *fmt, int v)
{
        static char buf[128];

        while (lp->s != NULL) {
                if (lp->v == v)
                        return (lp->s);
                ++lp;
        }
        if (fmt == NULL)
                fmt = "#%d";
        (void)sprintf(buf, fmt, v);
        return (buf);
}


/*
 * end print_util.c 
 */
