#include <xrmonwatch.h>
#include <atalk.h>

#include <net/if.h>
#include <netinet/in_systm.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netat/appletalk.h>
#include <netinet/rvd.h>
#include <X11/IntrinsicP.h>

#ifdef ZEPHYR
#include <zephyr/zephyr.h>
#endif /* ZEPHYR */


extern char *headerP;
char lbuf[BUFSIZ];

#ifdef sun
#define ETHERTYPE_RARP ETHERTYPE_REVARP
#endif /* sun */

typedef struct
{
  short   htype;
  short   ptype;
  u_char  hlen;
  u_char  plen;
  short   func;
} aarp_header;

typedef struct 
{
  unsigned char control;
  unsigned char sequence;
  short tid;
} atp_header;

typedef struct
{
  unsigned short connid;
  int first_seq;
  int next_seq;
  unsigned short window;
} adsp_header;

#define AARP_REQ   1 
#define AARP_RESP  2
#define AARP_PROBE 3

#define ZIP_QUERY  1
#define ZIP_REPLY  2
#define ZIP_DOWN   3
#define ZIP_UP     4

static char *lookup_int_type();

static char *do_ether_version();
static char *do_ether_vendor();
static char *do_ether_mcast();
static char *do_llc_pf();
static char *do_llc_src_addr_desig();
static char *do_llc_dst_addr_desig();
static char *do_llc_format();
static char *do_llc_supervisory();
static char *do_llc_command();
static char *do_ip_flags();
static char *do_ip_tos();
static char *do_icmp_type();
static char *do_icmp_code();
static char *do_llc_src_addr_desig();
static char *do_llc_dst_addr_desig();
static char *do_llc_format();
static char *do_rtmpreq_type();
static char *do_adsp_descriptor();
static char *do_adsp_control();

#ifdef ZEPHYR
static char *do_udp_zclt();
#endif /* ZEPHYR */

static char *do_llc();
static char *do_snap();
static char *do_arp();
static char *do_loopback();
static char *do_decnet();
static char *do_mop();
static char *do_chaos();
static char *do_xns();
static char *do_aarp();
static char *do_ip();
static char *do_ip_icmp();
static char *do_ip_rvd();
static char *do_ip_udp();
static char *do_ip_tcp();
static char *do_tcp_flags();
static char *do_ddp();
static char *do_shortddp();
static char *do_ddp_rtmp();
static char *do_ddp_rtmpreq();
static char *do_ddp_echo();
static char *do_ddp_nbp();
static char *do_ddp_zip();
static char *do_ddp_adsp();
static char *do_random_data();

static AtNode *do_atalk_responder();

static int itype;                /* hack used for colors */
extern int row;
extern char tbuf[];              /* storage for formatted txt */

#define copy_bits(from, len, to, size)   \
  {bcopy(*(from), to, min(*len, size));  \
  *(from) += size;                       \
  *(len)  -= size;}

/*
 * concat the string and leave the txt ptr at the trailing null
 */

#define add_string(txt, s)               \
  { strcat(txt, s);                      \
  txt += strlen(s); }

/*
 * macro to extract hardware address from ethernet packet 
 */

#ifdef sun
#define get_ether_addr(e)  e.ether_addr_octet               
#else  /* sun */
#define get_ether_addr(e)  e  
#endif /* sun */



char *
do_packet(data, len, column, color)
     char *data;                 /* incoming packet */
     int len;                    /* incoming packet length */
     int column;                 /* display for columns or text buffer */
     int *color;                 /* return color resource to use */
{
  struct ether_header ether;
  unsigned short type;
  char *txt;
  char *mcast = (char *) NULL;
  int init = 0;

  if((parts[R_DDPSrcName].state || parts[R_DDPDstName].state ||
     parts[R_DDPSrcType].state || parts[R_DDPDstType].state ||
     parts[R_DDPSrcZone].state || parts[R_DDPDstZone].state) && !init++)
    at_nbpUpdate();

  /*
   * make sure we have enough data 
   */

  if(len < sizeof(ether))
    return((char *) NULL);

  /*
   * initialize text buffer
   */

  txt = tbuf;
  if(!column)
    *txt++ = '\n';
  *txt = '\0';

  copy_bits(&data, &len, &ether, sizeof(ether));
  add_string(headerP, "   ");

  /*
   * figure out if we have an ethernet or 802.3 packet. 
   */

  if(ntohs(ether.ether_type) > 1500)    
    type = ntohs(ether.ether_type);
  else
    type = ETHERTYPE_LLC;

  /*
   * format ether fields
   */

  txt = format_str(R_Ethernet, "", column, LEFT, txt);
  txt = format_str(R_EthernetVersion, 
		   do_ether_version(type), column, LEFT, txt);
  txt = format_nstr(R_EthernetType, type, 
		    lookup_int_type(type, column, ether_types), column, 
		   LEFT, txt);
  
  if(parts[R_EthernetSrcVendor].state || !column)
    txt = format_str(R_EthernetSrcVendor, 
		     do_ether_vendor(get_ether_addr(ether.ether_shost), 
				     column), column, RIGHT, txt);

  txt = format_ether_addr(R_EthernetSrcAddress, 
			  get_ether_addr(ether.ether_shost), 
			  column, RIGHT, txt);
  
  if(column)
    if((parts[R_EthernetSrcAddress].state || 
	parts[R_EthernetSrcVendor].state) &&
       (parts[R_EthernetDstAddress].state || 
	parts[R_EthernetDstVendor].state))
      {
	add_string(txt, "-> ");
	add_string(headerP, "-> ");
      }

  txt = format_ether_addr(R_EthernetDstAddress, 
			  get_ether_addr(ether.ether_dhost),
			  column, LEFT, txt);
  
  if(parts[R_EthernetDstVendor].state || !column)
    txt = format_str(R_EthernetDstVendor, 
		     do_ether_vendor(get_ether_addr(ether.ether_dhost), 
				     column),  column, LEFT, txt);

  if(parts[R_EthernetMulticast].state || !column)
    if(mcast = do_ether_mcast(ether.ether_dhost, column))  
      txt = format_str(R_EthernetMulticast, mcast, column, LEFT, txt);

  if(!column)
    add_string(txt, "\n");
  
  itype = R_Ethernet;

  switch(type)
    {
    case ETHERTYPE_IP:
      txt = do_ip(data, len, column, txt);
      break;
    case ETHERTYPE_ARP:
    case ETHERTYPE_RARP:
      txt = do_arp(data, len, column, txt);
      break;
    case ETHERTYPE_ELAP:
      txt = do_ddp(data, len, column, txt);
      break;
    case ETHERTYPE_AARP:
      txt = do_aarp(data, len, column, txt);
      break;
    case ETHERTYPE_LOOP:
      txt = do_loopback(data, len, column, txt);
      break;
    case ETHERTYPE_CHAOS:
      txt = do_chaos(data, len, column, txt);
      break;
    case ETHERTYPE_DECNET:
      txt = do_decnet(data, len, column, txt);
      break;
    case ETHERTYPE_MOP:
      txt = do_mop(data, len, column, txt);
      break;
    case ETHERTYPE_XNS:
      txt = do_xns(data, len, column, txt);
      break;
    case ETHERTYPE_LLC:
      txt = do_llc(data, len, column, txt);
      break; 
    default:
      itype = 0;
      txt = do_random_data(data, len, column, txt);
      break;
    }
  
  /*
   * choose a color 
   */

  while(itype &&  !parts[itype].color)
    itype = parts[itype].base;
  if(mcast)
    *color = parts[itype].highlight;
  else
    *color = parts[itype].color;

  if(!txt)
    return((char *) NULL);

  return(tbuf);
}


static char *
do_ether_version(type)
     unsigned short type;
{
  if(type < 1500)
    return("802.3");
  else
    return("v2");
}


/*
 *
 */

static char *
lookup_int_type(type, column, table)
     unsigned int type;
     int column;
     struct TABLE table;
{
  HashEntry *he;
  struct ROW *r;

  if(!table.rows)
    return("no table!");
  
  if((he = sh_fetch(table.hp, str_makeData(&type, sizeof(type)))) &&
     he->data.s)
    {
      r = (struct ROW *) he->data.s;
      return(column ? r->sname : r->name);
    }
  
  return("???");
}



static char *
do_ether_vendor(s, column)
     char *s;
     int column;
{
  HashEntry *he;
  struct ROW *r;
  char buf[16];

  if(!ether_addrs.rows)
    return("no table!");
  
  sprintf(buf, "%02x%02x%02x", s[0] & 0xff, s[1] & 0xff, s[2] & 0xff);

  if((he = sh_fetch(ether_addrs.hp, str_makeString(buf))) && he->data.s)
    {
      r = (struct ROW *) he->data.s;
      return(column ? r->sname : r->name);
    }

  return("???");
}


static char *
do_ether_mcast(s, column)
     char *s;
     int column;
{
  HashEntry *he;
  struct ROW *r;
  char buf[16];

  if(!ether_mcasts.rows)
    return("no table!");

  sprintf(buf, "%02x%02x%02x%02x%02x%02x", s[0] & 0xff, s[1] & 0xff,
	  s[2] & 0xff, s[3] & 0xff, s[4] & 0xff, s[5] & 0xff);
  
  if((he = sh_fetch(ether_mcasts.hp, str_makeString(buf))) &&
     he->data.s)
    {
      r = (struct ROW *) he->data.s;
      return(column ? r->sname : r->name);
    }
  
  return((char *) NULL);
}



static char *
do_llc(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  unsigned char src;
  unsigned char dst;
  unsigned char control;
  unsigned char hcontrol;

  if(len < 3)
    return(txt);

  copy_bits(&data, &len, &dst, sizeof(dst));
  copy_bits(&data, &len, &src, sizeof(src));
  copy_bits(&data, &len, &control, sizeof(control));

  if((control & 0x3) != 0x3)
    copy_bits(&data, &len, &hcontrol, sizeof(hcontrol));
  
  txt = format_str(R_LLC, "", column, LEFT, txt);
  txt = format_uchar(R_LLCSrcAddress, src >> 1, column, RIGHT, txt);

  if(column)
    if(parts[R_LLCSrcAddress].state || parts[R_LLCDstAddress].state)
      {
	add_string(txt, "-> ");
	add_string(headerP, "-> ");
      }

  txt = format_uchar(R_LLCDstAddress, dst >> 1, column, LEFT, txt);
  txt = format_nstr(R_LLCFormat, control & 0x3, do_llc_format(control & 0x3), 
		   column, LEFT, txt);
  txt = format_nstr(R_LLCSrcAddrDesig, src & 0x1, 
		    do_llc_src_addr_desig(src & 0x1), 
		     column, LEFT, txt);
  txt = format_nstr(R_LLCDstAddrDesig, dst & 0x1, 
		    do_llc_dst_addr_desig(dst & 0x1), 
		     column, LEFT, txt);
  
  if((control & 0x3) == 0)
    {
      txt = format_nstr(R_LLCPFBit, hcontrol & 0x1, 
			do_llc_pf(hcontrol & 0x1, src & 0x1),
		       column, LEFT, txt);
      txt = format_uchar(R_LLCSendSequence, control >> 1, column, LEFT, txt);
      txt = format_uchar(R_LLCRecvSequence, hcontrol >> 1, column, LEFT, txt);
    }

  if((control & 0x3) == 1)
    {
      txt = format_nstr(R_LLCPFBit, hcontrol & 0x1,
			do_llc_pf(hcontrol & 0x1, src & 0x1),
		       column, LEFT, txt);
      txt = format_nstr(R_LLCSupervisoryBit, (control >> 2) & 0x3,
		       do_llc_supervisory((control >> 2) & 0x3, column), 
		       column, LEFT, txt);
      txt = format_uchar(R_LLCRecvSequence, hcontrol >> 1, column, LEFT, txt);
    }

  if((control & 0x3) == 3)
    {
      txt = format_nstr(R_LLCPFBit, (control >> 4) & 0x1,
			do_llc_pf((control >> 4) & 0x1, src & 0x1),
			 column, LEFT, txt);
      txt = format_nstr(R_LLCCommand, control >> 2,
			do_llc_command(control >> 2, column), 
		       column, LEFT, txt);
      if(((control << 2) | 0x4) == 0x2f)
	{
	  data += 3;
	  len  -= 3;
	}
      if(((control >> 2) | 0x4) == 0x24)
	{
	  data += 5;
	  len  -= 5;
	}
    }      

  if(!column)
    add_string(txt, "\n");

  txt = do_snap(data, len, column, txt);
  return(txt);
}


static char *
do_llc_src_addr_desig(bit)
     unsigned char bit;
{
  if(bit)
    return("response");
  else
    return("command");
}


static char *
do_llc_dst_addr_desig(bit)
     unsigned char bit;
{
  if(bit)
    return("group");
  else
    return("individual");
}


static char *
do_llc_format(type)
     unsigned char type;
{
  switch(type)
    {
    case 0:
      return("information transfer");
    case 1:
      return("supervisory");
    case 3:
      return("unnumbered");
    default:
      return("???");
    }
}


static char *
do_llc_pf(bit, src)
     unsigned char bit;
     unsigned char src;
{
  if(src && bit)
    return("poll");
  if(bit)
    return("final");
  return("");
}


static char *
do_llc_supervisory(s, column)
     unsigned char s;
     int column;
{
  if(column)
    {
      switch(s)
	{
	case 0:
	  return("RR");
	case 1:
	  return("REJ");
	case 2:
	  return("RNR");
	default:
	  return("???");
	}
    }
  else
    {
      switch(s)
	{
	case 0:
	  return("receive ready (RR)");
	case 1:
	  return("reject (REJ)");
	case 2:
	  return("receive not ready (RNR)");
	default:
	  return("???");
	}
    }
}


static char *
do_llc_command(command, column)
     unsigned char command;
     int column;
{
  command |= 0x4;

  if(column)
    {
      switch(command)
	{
	case 0x1f:
	  return("SABME");
	case 0x14:
	  return("DISC");
	case 0x1b:
	  return("UA");
	case 0x7:
	  return("DM");
	case 0x24:
	  return("FRMR");
        case 0x4:
	  return("UI");
	case 0x2f:
	  return("XID");
	case 0x3f:
	  return("TEST");
	default:
	  return("???");
	}
    }
  else
    switch(command)
      {
      case 0x1f:
	return("set asynchronous balanced mode extended (SABME)");
      case 0x14:
	return("disconnect (DISC)");
      case 0x1b:
	return("unnumbered ack (UA)");
      case 0x7:
	return("disconnect mode (DM)");
      case 0x24:
	return("frame reject (FR)");
      case 0x4:
	return("unnumbered info (UI)");
      case 0x2f:
	return("exchange ident (XID)");
      case 0x3f:
	return("test");
      default:
	return("???");
      }
}



static char *
do_snap(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  char s[6];
  char t[32];

  /*
   * All I know about snap is that it usually is 5 bytes in length.
   */

  if(len < 5)
    return(txt);

  copy_bits(&data, &len, s, 5);
  sprintf(t, "%02x%02x%02x%02x%02x", s[0] & 0xff, s[1] & 0xff, s[2] & 0xff, 
	  s[3] & 0xff, s[4] & 0xff);
    
  txt = format_str(R_SNAP, "", column, LEFT, txt);
  txt = format_str(R_SNAPAddress, t, column, LEFT, txt);
  
 if(!column)
    add_string(txt, "\n");

  itype = R_SNAP;

  if(strcmp(t, "080007809b") == 0)
    txt = do_ddp(data, len, column, txt);
  else
    if(strcmp(t, "08000780f3") == 0)
      txt = do_aarp(data, len, column, txt);
    else
      txt = do_random_data(data, len, column, txt);

  return(txt);
}





static char *
do_arp(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  struct ether_arp arp;
  struct in_addr addr;

  if(len < sizeof(arp))
    return(txt);

  copy_bits(&data, &len, &arp, sizeof(arp));
  bcopy(arp.arp_spa, &addr.s_addr, sizeof(addr.s_addr));
  txt = format_str(R_ARP, "", column, LEFT, txt);
  txt = format_domain_name(R_ARPSrcName, addr, column, RIGHT, txt);
  txt = format_ip_addr(R_ARPSrcProtocolAddress, addr, column, RIGHT, txt);
  if(parts[R_ARPSrcVendor].state || !column)
    txt = format_str(R_ARPSrcVendor, 
		     do_ether_vendor(get_ether_addr(arp.arp_sha), 
				     column), column, RIGHT, txt);
  txt = format_ether_addr(R_ARPSrcPhysicalAddress,
			  get_ether_addr(arp.arp_sha), 
			  column, RIGHT, txt);

  if(column)
    if((parts[R_ARPSrcProtocolAddress].state || 
	parts[R_ARPSrcPhysicalAddress].state ||
	parts[R_ARPSrcName].state) &&
       (parts[R_ARPTargetProtocolAddress].state || 
	parts[R_ARPTargetPhysicalAddress].state ||
	parts[R_ARPTargetName].state))
      {
	add_string(txt, "-> ");
	add_string(headerP, "-> ");
      }

  bcopy(arp.arp_tpa, &addr.s_addr, sizeof(addr.s_addr));
  txt = format_ether_addr(R_ARPTargetPhysicalAddress, 
			  get_ether_addr(arp.arp_tha),
			  column, LEFT, txt);
  if(parts[R_ARPTargetVendor].state || !column)
    txt = format_str(R_ARPTargetVendor, 
		     do_ether_vendor(get_ether_addr(arp.arp_tha), 
				     column), column, LEFT, txt);
  txt = format_ip_addr(R_ARPTargetProtocolAddress, addr, column, LEFT, txt);
  txt = format_domain_name(R_ARPTargetName, addr, column, LEFT, txt);

  itype = R_ARP;
  if(!column)
    add_string(txt, "\n");

  return(txt);
}


static char *
do_loopback(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  itype = R_LOOP;
  txt = do_random_data(data, len, column, txt);
  return(txt);
}


static char *
do_ip(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  struct ip ip;

  if(len < sizeof(ip))
    return(txt);

  copy_bits(&data, &len, &ip, sizeof(ip));

  txt = format_str(R_IP, "", column, LEFT, txt);
  txt = format_domain_name(R_IPSrcName, ip.ip_src, column, RIGHT, txt);
  txt = format_ip_addr(R_IPSrcAddress, ip.ip_src, column, RIGHT, txt);
  
  if(column)
    if((parts[R_IPSrcAddress].state || parts[R_IPSrcName].state) &&
       (parts[R_IPDstAddress].state || parts[R_IPDstName].state))
      {
	add_string(txt, "-> ");
	add_string(headerP, "-> ");
      }

  txt = format_ip_addr(R_IPDstAddress, ip.ip_dst, column, LEFT, txt);
  txt = format_domain_name(R_IPDstName, ip.ip_dst, column, LEFT, txt);
  txt = format_uchar(R_IPVersion, ip.ip_v, column, LEFT, txt);
  txt = format_uchar(R_IPHeaderLength, ip.ip_hl, column, LEFT, txt);
  txt = format_uint(R_IPLength, ntohs(ip.ip_len), column, LEFT, txt);
  txt = format_uint(R_IPIdentification, ntohs(ip.ip_id), column, LEFT, txt);
  txt = format_uint(R_IPTTL, ip.ip_ttl, column, LEFT, txt);
  txt = format_uint(R_IPChecksum, ntohs(ip.ip_sum), column, LEFT, txt);
  txt = format_uint(R_IPFragmentOffset, ntohs(ip.ip_off) & ~0x14000, 
		    column, LEFT, txt);
  txt = format_nstr(R_IPFlags, ntohs(ip.ip_off) & 0x14000,
		    do_ip_flags(ntohs(ip.ip_off) & 0x14000),
		    column, LEFT, txt);
  txt = format_nstr(R_IPType, ip.ip_tos,
		    do_ip_tos(ip.ip_tos), column, LEFT, txt);
  txt = format_nstr(R_IPProtocol, ip.ip_p, 
		    lookup_int_type(ip.ip_p, column, ip_protos), 
		    column, LEFT, txt);


  if(!column)
    add_string(txt, "\n");

  itype = R_IP;

  switch(ip.ip_p)
   {
   case IPPROTO_ICMP: 
     txt = do_ip_icmp(data, len, column, txt);
     break;
#ifdef notdef
#ifdef sun
   case IPPROTO_IGMP:
     txt = do_ip_igmp(data, len, column, txt);
     break;
#endif
   case IPPROTO_GGP:
     txt = do_ip_ggp(data, len, column, txt);
     break;
#endif
   case IPPROTO_TCP:
     txt = do_ip_tcp(data, len, column, txt);
     break;
#ifdef notdef
   case IPPROTO_EGP:
     txt = do_ip_egp(data, len, column, txt);
     break;
   case IPPROTO_PUP:
     txt = do_ip_pup(data, len, column, txt);
     break;
#endif
   case IPPROTO_UDP:
     txt = do_ip_udp(data, len, column, txt);
     break;
#ifdef notdef
   case IPPROTO_IDP:
     txt = do_ip_idp(data, len, column, txt);
     break;
#ifdef sun
   case IPPROTO_HELLO:
     txt = do_ip_hello(data, len, column, txt);
     break;
#endif
   case IPPROTO_RAW:
     txt = do_ip_raw(data, len, column, txt);
     break;
#endif
   case IPPROTO_RVD:
     txt = do_ip_rvd(data, len, column, txt);
     break;
   default:
     txt = do_random_data(data, len, column, txt);
     break;
   } 
  return(txt);
}


static char *
do_ip_flags(flags)
     short flags;
{
  if(flags & IP_DF)
    return("don't fragment");
  if(flags & IP_MF)
    return("more fragments");
  return("");
}



static char *
do_ip_tos(type)
     unsigned char type;
{
  static char buf[100];

  buf[0] = '\0';
  switch(type & 0x30)
    {
    case 0:
      sprintf(buf, "routine");
      break;
    case 1:
      sprintf(buf, "priority");
      break;
    case 2:
      sprintf(buf, "immediate");
      break;
    case 3:
      sprintf(buf, "flash");
      break;
    case 4:
      sprintf(buf, "flash override");
      break;
    case 5:
      sprintf(buf, "critic/ecp");
      break;
    case 6:
      sprintf(buf, "internet control");
      break;
    case 7:
      sprintf(buf, "network control");
      break;
    default:
      sprintf(buf, "???");
    }

  if(type & 0x40)
    {
      if(*buf)
	strcat(buf, " ,");
      strcat(buf, "speed");
    }
  if(type & 0x50)
    {
      if(*buf)
	strcat(buf, " ,");
      strcat(buf, "thorughput");
    }
  if(type & 0x60)
    {
      if(*buf)
	strcat(buf, " ,");
      strcat(buf, "reliability");
    }
  return(buf);
}




static char *
do_aarp(data, len, column, txt)
     char *data;
     int column;
     int len;
     char *txt;
{
  aarp_header ah;
  char e[100];
  int paddr;

  if(len < sizeof(ah))
    return(txt);

  copy_bits(&data, &len, &ah, sizeof(ah));
  
  if(len < (ah.hlen + ah.plen))
     return(txt);

  copy_bits(&data, &len, e, min(sizeof(e), ah.hlen));  /* we can lose here */
  copy_bits(&data, &len, &paddr, min(sizeof(paddr), ah.plen));

  txt = format_str(R_AARP, "", column, LEFT, txt);
  txt = format_ether_addr(R_AARPSrcHardwareAddress, e, column, RIGHT, txt);
  txt = format_int(R_AARPSrcProtocolAddress, ntohl(paddr), column, 
		    RIGHT, txt);

  if(column)
    if((parts[R_AARPSrcHardwareAddress].state || 
	parts[R_AARPSrcProtocolAddress].state) &&
       (parts[R_AARPDstHardwareAddress].state || 
	parts[R_AARPDstProtocolAddress].state))
      {
	add_string(txt, "-> ");
	add_string(headerP, "-> ");
      }

  if(len < (ah.hlen + ah.plen))
     return(txt);
  
  copy_bits(&data, &len, e, min(sizeof(e), ah.hlen));
  copy_bits(&data, &len, &paddr, min(sizeof(paddr), ah.plen));

  txt = format_uint(R_AARPDstProtocolAddress, ntohl(paddr), column,  
		    LEFT, txt);
  txt = format_ether_addr(R_AARPDstHardwareAddress, e, column, LEFT, txt);

  txt = format_nstr(R_AARPType, ah.func, 
		    lookup_int_type(ah.func, column, aarp_types), 
		    column, LEFT, txt);
  txt = format_uint(R_AARPHardwareType, ntohs(ah.htype), column, LEFT, txt);
  txt = format_uint(R_AARPProtocolType, ntohs(ah.ptype), column, LEFT, txt);
  txt = format_uchar(R_AARPHardwareLength, ah.hlen, column, LEFT, txt);
  txt = format_uchar(R_AARPProtocolLength, ah.plen, column, LEFT, txt);

  if(!column)
    add_string(txt, "\n");
  return(txt);
}

  

static char *
do_shortddp(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  ShortDDP ddp;

  if(len < sizeof(ddp))
    return(txt);

  copy_bits(&data, &len, &ddp, sizeof(ddp));

  txt = format_str(R_DDP, "", column, LEFT, txt);
  txt = format_uint(R_DDPSrcSocket, ddp.srcSkt, column, RIGHT, txt);

  if(column)
    if(parts[R_DDPSrcSocket].state && parts[R_DDPDstSocket].state)
      {
	add_string(txt, "-> ");
	add_string(headerP, "-> ");
      }

  txt = format_uint(R_DDPDstSocket, ddp.dstSkt, column, LEFT, txt);
  txt = format_uint(R_DDPLength, ddp.length, column, LEFT, txt);
  txt = format_nstr(R_DDPType, ddp.type, 
		    lookup_int_type(ddp.type, column, ddp_protos), 
		    column, LEFT, txt);
  if(!column)
    add_string(txt, "\n");

  itype = R_DDP;

  switch(ddp.type)
    {
    case ddpRTMP:
      txt = do_ddp_rtmp(data, len, column, txt);
      break;
    case ddpNBP:
      txt = do_ddp_nbp(data, len, column, txt);
      break;
#ifdef notdef
    case ddpATP:
      txt = do_ddp_atp(data, len, column, txt);
      break;
#endif
    case ddpECHO:
      txt = do_ddp_echo(data, len, column, txt);
      break;
    case ddpRTMP_REQ:
      txt = do_ddp_rtmpreq(data, len, column, txt);
      break;
    case ddpZIP:
      txt = do_ddp_zip(data, len, column, txt);
      break;
    case ddpADSP:
      txt = do_ddp_adsp(data, len, column, txt);
      break;
    }

  return(txt);
}


static char *
do_ddp(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  DDP ddp;
  short hop;
  short l;
  AtNode *n;

  if(len < sizeof(ddp)-1)
    return(txt);

  copy_bits(&data, &len, &ddp, sizeof(ddp));
  --data; ++len; /* ddp structure not aligned */

  /* 
   * the length is a 10 bit number where the high 2 bits share
   * the hop count byte. What a crock.
   */

  l     = ntohs(ddp.length);
  hop   = l;
  l    &= 0x3ff;
  hop  &= ~0x3ff;
  hop  = hop >> 10;

  /*
   * sanity check the length against the ddp packet length
   */
/*
  if(len > l - sizeof(ddp))
    len   = l - sizeof(ddp);
*/

  n = do_atalk_responder(ntohs(ddp.srcNet), ddp.srcNode);

  txt = format_str(R_DDP, "", column, LEFT, txt);
  txt = format_str(R_DDPSrcName, n->name.s, column, RIGHT, txt);
  txt = format_str(R_DDPSrcType, n->type.s, column, RIGHT, txt);
  txt = format_str(R_DDPSrcZone, n->zone.s, column, RIGHT, txt);  
  txt = format_uint(R_DDPSrcNetwork, ntohs(ddp.srcNet), column, RIGHT, txt);
  txt = format_uint(R_DDPSrcNode, ddp.srcNode, column, RIGHT, txt);
  
  if(column)
    if((parts[R_DDPSrcName].state || parts[R_DDPSrcType].state ||
	parts[R_DDPSrcZone].state || parts[R_DDPSrcNetwork].state || 
       parts[R_DDPSrcNode].state) &&
       (parts[R_DDPDstNetwork].state || parts[R_DDPDstNode].state ||
	parts[R_DDPDstName].state || parts[R_DDPDstType].state ||
	parts[R_DDPDstZone].state))	  
      {
	add_string(txt, "-> ");
	add_string(headerP, "-> ");
      }

  n = do_atalk_responder(ntohs(ddp.dstNet), ddp.dstNode);

  txt = format_uint(R_DDPDstNetwork, ntohs(ddp.dstNet), column, LEFT, txt);
  txt = format_uint(R_DDPDstNode, ddp.dstNode, column, LEFT, txt);
  txt = format_str(R_DDPDstName, n->name.s, column, LEFT, txt);
  txt = format_str(R_DDPDstType, n->type.s, column, LEFT, txt);
  txt = format_str(R_DDPDstZone, n->zone.s, column, LEFT, txt); 
  txt = format_uint(R_DDPLength, l, column, LEFT, txt);
  txt = format_nstr(R_DDPType, ddp.type, 
		    lookup_int_type(ddp.type, column, ddp_protos), 
		    column, LEFT, txt);

  txt = format_uint(R_DDPSrcSocket, ddp.srcSkt, column, RIGHT, txt);
  if(column)
    if(parts[R_DDPSrcSocket].state &&  parts[R_DDPDstSocket].state)
      {
	add_string(txt, "-> ");
	add_string(headerP, "-> ");
      }

  txt = format_uint(R_DDPDstSocket, ddp.dstSkt, column, LEFT, txt);
  txt = format_uint(R_DDPChecksum, ntohs(ddp.checksum), column, LEFT, txt);
  txt = format_uint(R_DDPHopCount, hop, column, LEFT, txt);

  if(!column)
    add_string(txt, "\n");

  itype = R_DDP;

  switch(ddp.type)
    {
    case ddpRTMP:
      txt = do_ddp_rtmp(data, len, column, txt);
      break;
    case ddpNBP:
      txt = do_ddp_nbp(data, len, column, txt);
      break;
#ifdef notdef
    case ddpATP:
      txt = do_ddp_atp(data, len, column, txt);
      break;
#endif
    case ddpECHO:
      txt = do_ddp_echo(data, len, column, txt);
      break;
    case ddpRTMP_REQ:
      txt = do_ddp_rtmpreq(data, len, column, txt);
      break;
    case ddpZIP:
      txt = do_ddp_zip(data, len, column, txt);
      break;
    case ddpADSP:
      txt = do_ddp_adsp(data, len, column, txt);
      break;
    }
  return(txt);
}




static AtNode *
do_atalk_responder(network, node)
     short network;
     unsigned char node;
{
  HashEntry *h;
  Rope str;
  static int zoning = 0;
  static XtIntervalId id = 0;
  extern XtAppContext app_con;
  static AtNode n;
  static char hash[16];

  /*
   * turn the appletalk updating on if user wishes to display stuff 
   */

  if((parts[R_DDPSrcName].state || parts[R_DDPSrcType].state ||
     parts[R_DDPSrcZone].state || parts[R_DDPDstName].state || 
     parts[R_DDPDstType].state || parts[R_DDPDstZone].state) && !zoning)
    {
      id = XtAppAddTimeOut(app_con, 500, cb_atNBPUpdate, NULL);
      zoning = 1;
    }

  if((!parts[R_DDPSrcName].state && !parts[R_DDPSrcType].state &&
     !parts[R_DDPSrcZone].state && !parts[R_DDPDstName].state &&
     !parts[R_DDPDstType].state && !parts[R_DDPDstZone].state) && zoning)
    {
      XtRemoveTimeOut(id);
      zoning = 0;
    }

  sprintf(hash, "%d.%d", network, node);
  str = str_makeString(hash);
  if(nodes)
    if((h = sh_fetch(nodes, str)) && h->data.s)
      return((AtNode *) h->data.s);

  /*
   * return something 
   */

  bzero(&n, sizeof(n));
  n.name = str;
  return(&n);
}




static char *
do_ddp_rtmp(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  RTMP rtmp;
  int i = 0;
  unsigned short start;
  unsigned short end;
  unsigned char version;
  unsigned char distance;

  if(len < sizeof(rtmp))
    return(txt);

  copy_bits(&data, &len, &rtmp, sizeof(rtmp));

  txt = format_str(R_RTMP, "", column, LEFT, txt);
  txt = format_uint(R_RTMPSrcNetwork, ntohs(rtmp.net), column, LEFT, txt);
  txt = format_uint(R_RTMPSrcNodeLength, rtmp.idLen, column, LEFT, txt);
  txt = format_uint(R_RTMPSrcNode, rtmp.id, column, LEFT, txt);
  
  /*
   * may only work for extended network case. I'm not dealing
   * with the version field on the non extended case. 
   */

  while(len && ((unsigned char) data[5] == 0x82))
    {
      if(len < sizeof(start) + sizeof(distance))
	return(txt);

      copy_bits(&data, &len, &start, sizeof(start));
      copy_bits(&data, &len, &distance, sizeof(distance));
      
      if(!column)
	{
	  ++i;
	  sprintf(txt, "Tuple %d:\n", i);
	  txt += strlen(txt);
	}

      /*
       * high order distance bit indicates extended network 
       */

      if(distance & 0x80)
	{
	  txt = format_uint(R_RTMPRangeStart, ntohs(start), column, 
			    RIGHT, txt);
	  if(column)
	    if(parts[R_RTMPRangeStart].state && parts[R_RTMPRangeEnd].state)
	      add_string(txt, "- ");

	  if(len < sizeof(end))
	    return(txt);
	  copy_bits(&data, &len, &end, sizeof(end));

	  txt = format_uint(R_RTMPRangeEnd, ntohs(end), column, LEFT, txt);
	  txt = format_uchar(R_RTMPDistance, distance & 0x7f, column, 
			     LEFT, txt);

	  if(len < sizeof(version))
	    return(txt);
	  copy_bits(&data, &len, &version, sizeof(version));
	  txt = format_uchar(R_RTMPVersion, version, column, LEFT, txt);
	}
      else
	{
	  txt = format_uint(R_RTMPRangeStart, ntohs(start), column, 
			    RIGHT, txt);
	  txt = format_uchar(R_RTMPDistance, distance, column, LEFT, txt);
	}      
      
      /*
       * not dealing with variable lentgh columns
       */
      if(column)
	break;
    }
  return(txt);
}



static char *
do_ddp_rtmpreq(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  unsigned char c;

  if(len < sizeof(c))
    return(txt);

  copy_bits(&data, &len, &c, sizeof(c));

  txt = format_str(R_RTMP, "", column, LEFT, txt);
  txt = format_nstr(R_RTMPFunction, c, do_rtmpreq_type(c, column), column, 
		   LEFT, txt);
  return(txt);
}


static char *
do_rtmpreq_type(type, column)
     unsigned char type;
     int column;
{
  if(column)
    switch(type)
      {
      case 1:
	return("request");
      case 2:
	return("route data request/split horizon");
      case 3:
	return("route data request/whole horizon");
      default:
	return("???");
      }
  else
    switch(type)
      {
      case 1:
	return("req");
      case 2:
	return("rdr/sp");
      case 3:
	return("rdr");
      default:
	return("???");
      }
}


static char *
do_ddp_echo(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  unsigned char type;

  if(len < sizeof(type))
    return(txt);
  copy_bits(&data, &len, &type, sizeof(type));

  txt = format_str(R_AEP, "", column, LEFT, txt);
  txt = format_nstr(R_AEPType, type, 
		    lookup_int_type(type, column, echo_types), 
		    column, LEFT, txt);
  txt = do_random_data(data, len, column, txt);
  return(txt);
}



static char *
do_ddp_nbp(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  unsigned char ntuples;
  unsigned char function;
  unsigned char id;
  unsigned char socket;
  unsigned char enu;
  unsigned char length;
  short net;
  char object[100];
  int i = 0;

  if(len < sizeof(ntuples) + sizeof(id))
    return(txt);
  copy_bits(&data, &len, &ntuples, sizeof(ntuples));
  copy_bits(&data, &len, &id, sizeof(id));

  /*
   * the lower four bits of the tuples byte is the number
   * of tuples in the packet. The high four is a function type
   */

  function = (ntuples & 0xf0) >> 4;
  ntuples &= 0x0f;
  
  txt = format_str(R_NBP, "", column, LEFT, txt);
  txt = format_nstr(R_NBPType, function, 
		    lookup_int_type(function, column, nbp_types), column,
		    LEFT, txt);
  txt = format_uchar(R_NBPTupleCount, ntuples, column, LEFT, txt);
  txt = format_uchar(R_NBPIdentification, id, column, LEFT, txt);
  
  while(ntuples && len)
    {
      if(len < sizeof(net) + sizeof(id) + sizeof(socket) + sizeof(enu))
	return(txt);

      copy_bits(&data, &len, &net, sizeof(net));
      copy_bits(&data, &len, &id, sizeof(id));
      copy_bits(&data, &len, &socket, sizeof(socket));
      copy_bits(&data, &len, &enu, sizeof(enu));

      if(!column)
	{
	  ++i;
	  sprintf(txt, "Tuple %d:\n", i);
	  txt += strlen(txt);
	}

      txt = format_uint(R_NBPNetwork, ntohs(net), column, LEFT, txt);
      txt = format_uchar(R_NBPNode, id, column, LEFT, txt);
      txt = format_uchar(R_NBPSocket, socket, column, LEFT, txt);
      txt = format_uchar(R_NBPEnumerator, enu, column, LEFT, txt);

      /*
       * extract three variable length strings and their lengths
       */

      if(len < sizeof(length))
	return(txt);

      bzero(object, sizeof(object));
      copy_bits(&data, &len, &length, sizeof(length));
      if(len < length)
	return(txt);
      copy_bits(&data, &len, object, min(sizeof(object), length));
      txt = format_uchar(R_NBPObjectLength, length, column, LEFT, txt);
      txt = format_str(R_NBPObjectEntity, object, column, LEFT, txt);

      bzero(object, sizeof(object));
      copy_bits(&data, &len, &length, sizeof(length));
      if(len < length)
	return(txt);
      copy_bits(&data, &len, object, min(sizeof(object), length));
      txt = format_uchar(R_NBPTypeLength, length, column, LEFT, txt);
      txt = format_str(R_NBPTypeEntity, object, column, LEFT, txt);

      copy_bits(&data, &len, &length, sizeof(length));
      txt = format_uchar(R_NBPZoneLength, length, column, LEFT, txt);
      if(length)
	{
	  if(len < length)
	    return(txt);
	  bzero(object, sizeof(object));
	  copy_bits(&data, &len, object, min(sizeof(object), length));
	  txt = format_str(R_NBPZoneEntity, object, column, LEFT, txt);
	}
      else	
	txt = format_str(R_NBPZoneEntity, "*", column, LEFT, txt);

      --ntuples;
    }
  return(txt);
}



static char *
do_ddp_zip(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  unsigned char type;
  unsigned char count;
  unsigned char length;
  short network;
  char name[100];

  if(len < sizeof(type) + sizeof(count) + sizeof(length))
    return(txt);

  copy_bits(&data, &len, &type, sizeof(type));
  copy_bits(&data, &len, &count, sizeof(count));

  txt = format_str(R_ZIP, "", column, LEFT, txt);
  txt = format_nstr(R_ZIPType, type, 
		    lookup_int_type(type, column, zip_types), 
		    column, LEFT, txt);
  txt = format_uchar(R_ZIPNetworkCount, count, column, LEFT, txt);

  if(type == ZIP_QUERY)
    while(len && count)
      {
	copy_bits(&data, &len, &network, sizeof(network));
	txt = format_uint(R_ZIPNetwork, network, column, LEFT, txt);
	--count;
      }

  if((type == ZIP_REPLY) || (type == ZIP_UP))
    while(len && count)
	{
	  copy_bits(&data, &len, &network, sizeof(network));
	  txt = format_uint(R_ZIPNetwork, network, column, LEFT, txt);
	  copy_bits(&data, &len, &length, sizeof(length));
	  bzero(name, sizeof(name));
	  copy_bits(&data, &len, name, min(sizeof(name), length));
	  txt = format_uchar(R_ZIPZoneLength, length, column, LEFT, txt);
	  txt = format_str(R_ZIPZone, name, column, LEFT, txt);
	  --count;
	}

  return(txt);
}


#ifdef notdef
static char *
do_ddp_atp(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  atp_header ah;

  bcopy(data, &ah, sizeof(ah));
  data += sizeof(ah);
  len  -= sizeof(ah);

  
  return(txt);
}

#endif

static char *
do_ddp_adsp(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  adsp_header ah;
  unsigned char d;
  unsigned short code;

  if(len < sizeof(ah))
    return(txt);

  copy_bits(&data, &len, &ah, sizeof(ah));
 
  /*
   * descriptor
   */

  copy_bits(&data, &len, &d, sizeof(d));
  
  txt = format_str(R_ADSP, "", column, LEFT, txt);
  txt = format_uint(R_ADSPConnId, ah.connid, column, LEFT, txt);
  txt = format_uint(R_ADSPFirstSeq, ah.first_seq, column, LEFT, txt);
  txt = format_uint(R_ADSPNextSeq, ah.next_seq, column, LEFT, txt);
  txt = format_uint(R_ADSPWindow, ah.window, column, LEFT, txt);
  txt = format_nstr(R_ADSPDescriptor, d, do_adsp_descriptor(d), column, 
		    LEFT, txt);
  if(d & 0x40)
    {
      copy_bits(&data, &len, &code, sizeof(code));
      txt   = format_nstr(R_ADSPControlCode, d & 0x40, 
			  lookup_int_type(d & 0x40, column, adsp_types), 
			  column, LEFT, txt);
    }
  return(do_random_data(data, len, column, txt));
}


static char *
do_adsp_descriptor(d)
     unsigned char d;
{
  char buf[100];

  sprintf(buf, "%s%s%s%s", d & 0x80 ? "control " : "",
	  d & 0x70 ? "ack req " : "", d & 0x60 ? "EOM " : "", 
	  d & 0x50 ? "Att. " : "");
  return(buf);
}


static char *
do_chaos(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  itype = R_CHAOS;
  txt = do_random_data(data, len, column, txt);
  return(txt);
}


static char *
do_decnet(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  itype = R_DECNET;
  txt = do_random_data(data, len, column, txt);
  return(txt);
}


static char *
do_mop(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  itype = R_MOP;
  txt = do_random_data(data, len, column, txt);
  return(txt);
}


static char *
do_xns(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{

  itype = R_XNS;
  txt = do_random_data(data, len, column, txt);
  return(txt);
}


static char *
do_ip_icmp(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  struct icmp icmp;

  if(len < sizeof(icmp))
    return(txt);

  copy_bits(&data, &len, &icmp, sizeof(icmp));

  txt = format_str(R_ICMP, "", column, LEFT, txt);
  txt = format_nstr(R_ICMPCode, icmp.icmp_code, 
		    do_icmp_code(icmp.icmp_type, icmp.icmp_code), 
		    column, RIGHT, txt);
  txt = format_nstr(R_ICMPType, icmp.icmp_type, 
		    lookup_int_type(icmp.icmp_type, column, icmp_types), 
		    column, LEFT,txt);
  txt = format_uint(R_ICMPChecksum, ntohs(icmp.icmp_cksum), column, LEFT, txt);

  if((icmp.icmp_type == ICMP_ECHOREPLY) ||
     (icmp.icmp_type == ICMP_ECHO) ||
     (icmp.icmp_type == ICMP_TSTAMP) ||
     (icmp.icmp_type == ICMP_TSTAMPREPLY) ||
     (icmp.icmp_type == ICMP_IREQ) ||
     (icmp.icmp_type == ICMP_IREQREPLY))
    {
      txt = format_uint(R_ICMPSequence, ntohs(icmp.icmp_seq), column, 
			LEFT, txt);
      txt = format_uint(R_ICMPIdentifier, ntohs(icmp.icmp_id), column, 
			LEFT, txt);
    }
  
  if(icmp.icmp_type == ICMP_REDIRECT)
    txt = format_ip_addr(R_ICMPGateway, icmp.icmp_gwaddr, column, LEFT, txt);

  itype = R_ICMP;
  if(!column)
    add_string(txt, "\n");

  return(txt);
}


static char *
do_icmp_code(type, code)
     unsigned char type;
     unsigned char code;
{
  switch(type)
    {
    case ICMP_UNREACH:
      switch(code)
	{
	case ICMP_UNREACH_NET:
	  return("network");
	case ICMP_UNREACH_HOST:
	  return("host");
	case ICMP_UNREACH_PROTOCOL:
	  return("protocol");
	case ICMP_UNREACH_PORT:
	  return("port");
	case ICMP_UNREACH_NEEDFRAG:
	  return("need frag");
	case ICMP_UNREACH_SRCFAIL:
	  return("src route");
	default:
	  return("unknown");
	}
    case ICMP_REDIRECT:
      switch(code)
	{
	case ICMP_REDIRECT_NET:
	  return("network");
	case ICMP_REDIRECT_HOST:
	  return("host");
	case ICMP_REDIRECT_TOSNET:
	  return("tos & net");
	case ICMP_REDIRECT_TOSHOST:
	  return("tos & host");
	default:
	  return("unknown");
	}
    case ICMP_TIMXCEED:
      switch(code)
	{
	case ICMP_TIMXCEED_INTRANS:
	  return("transit");
	case ICMP_TIMXCEED_REASS:
	  return("reassembly");
	default:
	  return("unknown");
	}
    default:
      return("");
    }
}


static char *
do_ip_tcp(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  struct tcphdr tcp;

  if(len < sizeof(tcp))
    return(txt);
  
  copy_bits(&data, &len, &tcp, sizeof(tcp));

  txt = format_str(R_TCP, "", column, LEFT, txt);
  txt = format_tcp_port(R_TCPSrcPortName, tcp.th_sport, column, RIGHT, txt);
  txt = format_uint(R_TCPSrcPort, tcp.th_sport, column, RIGHT, txt);

  if(column)
    if((parts[R_TCPSrcPortName].state || parts[R_TCPSrcPort].state) &&
       (parts[R_TCPDstPortName].state || parts[R_TCPDstPort].state))
      {
	add_string(txt, "-> ");
	add_string(headerP, "-> ");
      }

  txt = format_uint(R_TCPDstPort, tcp.th_dport, column, LEFT, txt);
  txt = format_tcp_port(R_TCPDstPortName, tcp.th_dport, column, LEFT, txt);
  txt = format_uint(R_TCPChecksum, ntohs(tcp.th_sum), column, LEFT, txt);
  txt = format_uint(R_TCPSequence, ntohl(tcp.th_seq), column, LEFT, txt);
  txt = format_uint(R_TCPAck, ntohl(tcp.th_ack), column, LEFT, txt);
  txt = format_uchar(R_TCPOffset, ntohl(tcp.th_off), column, LEFT, txt);
  txt = format_uint(R_TCPWindow, ntohs(tcp.th_win), column, LEFT, txt);
  txt = format_uint(R_TCPUrgent, ntohs(tcp.th_urp), column, LEFT, txt);
  txt = format_nstr(R_TCPFlags, tcp.th_flags, do_tcp_flags(tcp.th_flags), 
		    column,  LEFT, txt);

  if(!column)
    add_string(txt, "\n");

  itype = R_TCP;
  txt = do_random_data(data, len, column, txt);
  return(txt);
}


static char *
do_tcp_flags(flag)
     unsigned char flag;
{
  static char buf[32];
  *buf = '\0';

  if(flag & TH_FIN)
    strcat(buf, "FIN ");
  if(flag & TH_SYN) 
    strcat(buf, "SYN ");
  if(flag & TH_RST)
    strcat(buf, "RST ");
  if(flag & TH_PUSH)
    strcat(buf, "PUSH ");
  if(flag & TH_ACK)
    strcat(buf, "ACK ");
  if(flag & TH_URG)
    strcat(buf, "URG");

  return(buf);
}



static char *
do_ip_udp(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  struct udphdr udp;

  if(len < sizeof(udp))
    return(txt);

  copy_bits(&data, &len, &udp, sizeof(udp));

  txt = format_str(R_UDP, "", column, LEFT, txt);
  txt = format_udp_port(R_UDPSrcPortName, udp.uh_sport, column, RIGHT, txt);
  txt = format_uint(R_UDPSrcPort, udp.uh_sport, column, RIGHT, txt);

  if(column)
    if((parts[R_UDPSrcPortName].state || parts[R_UDPSrcPort].state) &&
       (parts[R_UDPDstPortName].state || parts[R_UDPDstPort].state))
      {
	add_string(txt, "-> ");
	add_string(headerP, "-> ");
      }

  txt = format_uint(R_UDPDstPort, udp.uh_dport, column, LEFT, txt);
  txt = format_udp_port(R_UDPDstPortName, udp.uh_dport, column, LEFT, txt);
  txt = format_int(R_UDPLength, ntohs(udp.uh_ulen), column, LEFT, txt);
  txt = format_uint(R_UDPChecksum, ntohs(udp.uh_sum), column, LEFT, txt);

  if(!column)
    add_string(txt, "\n");

  itype = R_UDP;

  switch(udp.uh_dport)
    {
#ifdef ZEPHYR
    case 2103:
      txt = do_udp_zclt(data, len, column, txt);
      break;
#endif /* ZEPHYR */
    default:
      txt = do_random_data(data, len, column, txt);
    }
  return(txt);
}



#ifdef ZEPHYR
static char *
do_udp_zclt(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  ZNotice_t z;
  static char *ZNoticeKinds[] = { "UNSAFE", "UNACKED", "ACKED", "HMACK",
				    "HMCTL", "SERVACK", "SERVNAK",
				    "CLIENTACK", "STAT"};
  
  bzero(&z, sizeof(z));
  ZParseNotice(data, len, &z);
  
  txt = format_str(R_Zephyr, "", column, LEFT, txt);
  txt = format_str(R_ZPacket, z.z_packet, column, RIGHT, txt);
  txt = format_str(R_ZVersion, z.z_version, column, RIGHT, txt);
  txt = format_str(R_ZKind, ZNoticeKinds[z.z_kind], column, RIGHT, txt);

  if(((z.z_kind != UNSAFE) && (z.z_kind != UNACKED) && (z.z_kind != ACKED)) ||
     !*z.z_sender)
    {
      txt = do_random_data(z.z_message, z.z_message_len, column, txt);
      return(txt);
    }

  txt = format_str(R_ZSender, z.z_sender, column, RIGHT, txt);

  if(column)
    if((parts[R_ZSender].state) && (parts[R_ZRecipient].state))
      {
	add_string(txt, "-> ");
	add_string(headerP, "-> ");
      }
  
  if(!*z.z_recipient)
    txt = format_str(R_ZRecipient, "*", column, LEFT, txt);
  else
    txt = format_str(R_ZRecipient, z.z_recipient, column, LEFT, txt);
  txt = format_str(R_ZClass, z.z_class, column, LEFT, txt);
  txt = format_str(R_ZInstance, z.z_class_inst, column, LEFT, txt);
  txt = format_str(R_ZOpcode, z.z_opcode, column, LEFT, txt);
  txt = do_random_data(z.z_message, z.z_message_len, column, txt);

  return(txt);
  
}
#endif /* ZEPHYR */



static char *
do_ip_rvd(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  struct rvdhdr rvd;

  if(len < sizeof(rvd))
    return(txt);

  copy_bits(&data, &len, &rvd, sizeof(rvd));

  txt = format_str(R_RVD, "", column, LEFT, txt);
  txt = format_nstr(R_RVDType, rvd.rvd_type, 
		    lookup_int_type(rvd.rvd_type, column, rvd_types), 
		    column, RIGHT, txt);
  txt = format_uchar(R_RVDVersion, rvd.version, column, RIGHT, txt);
  txt = format_uint(R_RVDDrive,ntohl( rvd.drive), column, LEFT, txt);
  txt = format_uint(R_RVDIdentifier, ntohl(rvd.nonce), column, LEFT, txt);
  txt = format_uint(R_RVDIndex, ntohl(rvd.index), column, LEFT, txt);
  txt = format_uint(R_RVDChecksum, ntohl(rvd.cksum), column, LEFT, txt);

  if(!column)
    add_string(txt, "\n");

  itype = R_RVD;
  txt = do_random_data(data, len, column, txt);
  return(txt);
}


static char *
do_random_data(data, len, column, txt)
     char *data;
     int len;
     int column;
     char *txt;
{
  int n = 0;

  if(column)
    {
       while(len--)
         if(isprint(*data))
           printf("%3c", *data++);
    return(txt);
    }
  
  add_string(txt, "Data:\n");

  while(len--)
    {
      if(isprint(*data)) 
	sprintf(txt, "%3c", *data++);
      else
	sprintf(txt, "%3x", *data++ & 0xff);
      ++n;
      txt += 3;
      if(n  > 15)
	{
	  *txt++ = '\n';
	  *txt   = '\0';
	  n = 0;
	}
    }
  return(txt);
}
  
