/*
 * Copyright (c) 1990, by John Robert LoVerso.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by John Robert LoVerso.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * This implementaion has been influenced by the CMU SNMP release,
 * by Steve Waldbusser.  However, this shares no code with that system.
 * Additional ASN.1 insight gained from Marshall T. Rose's _The_Open_Book_.
 * Earlier forms of this implemention were derived and/or inspired by an
 * awk script originally written by C. Philip Wood of LANL (but later
 * heavily modified by John Robert LoVerso).  The copyright notice for
 * that work is preserved below, even though it may not rightly apply
 * to this file.
 *
 * This started out as a very simple program, but the incremental decoding
 * (into the BE structure) complicated things.
 *
 #			Los Alamos National Laboratory
 #
 #	Copyright, 1990.  The Regents of the University of California.
 #	This software was produced under a U.S. Government contract
 #	(W-7405-ENG-36) by Los Alamos National Laboratory, which is
 #	operated by the	University of California for the U.S. Department
 #	of Energy.  The U.S. Government is licensed to use, reproduce,
 #	and distribute this software.  Permission is granted to the
 #	public to copy and use this software without charge, provided
 #	that this Notice and any statement of authorship are reproduced
 #	on all copies.  Neither the Government nor the University makes
 #	any warranty, express or implied, or assumes any liability or
 #	responsibility for the use of this software.
 #	@(#)snmp.awk.x	1.1 (LANL) 1/15/90
 */
#ifndef lint
static char rcsid[] =
    "@(#) $Id: print-snmp.c,v 1.4 1993/04/22 20:32:30 martinh Exp $ (UW)";
#endif

#include <sys/param.h>
#include <sys/types.h>
#include <stdio.h>
#include <ctype.h>

#ifdef TCPVIEW
#include "tcpview.h"
#endif

#include "interface.h"
#include "addrtoname.h"
#include "snmp.h"

/*
 * Universal ASN.1 types
 * (we only care about the tag values for those allowed in the Internet SMI)
 */
static char *Universal[] = {
	"U-0",
	"Boolean",
	"Integer",
#define INTEGER 2
	"Bitstring",
	"String",
#define STRING 4
	"Null",
#define ASN_NULL 5
	"ObjID",
#define OBJECTID 6
	"ObjectDes",
	"U-8","U-9","U-10","U-11",	/* 8-11 */
	"U-12","U-13","U-14","U-15",	/* 12-15 */
	"Sequence",
#define SEQUENCE 16
	"Set"
};

/*
 * Application-wide ASN.1 types from the Internet SMI and their tags
 */
static char *Application[] = {
	"IpAddress",
#define IPADDR 0
	"Counter",
#define COUNTER 1
	"Gauge",
#define GAUGE 2
	"TimeTicks",
#define TIMETICKS 3
	"Opaque"
};

/*
 * Context-specific ASN.1 types for the SNMP PDUs and their tags
 */
static char *Context[] = {
	"GetRequest",
/* #define GETREQ 0 */
	"GetNextRequest",
/* #define GETNEXTREQ 1 */
	"GetResponse",
/* #define GETRESP 2 */
	"SetRequest",
/* #define SETREQ 3 */
	"Trap"
/* #define TRAP 4 */
};

/*
 * Private ASN.1 types
 * The Internet SMI does not specify any
 */
static char *Private[] = {
	"P-0"
};

/*
 * error-status values for any SNMP PDU
 */
static char *ErrorStatus[] = {
	"noError",
	"tooBig",
	"noSuchName",
	"badValue",
	"readOnly",
	"genErr"
};

/*
 * generic-trap values in the SNMP Trap-PDU
 */
static char *GenericTrap[] = {
	"coldStart",
	"warmStart",
	"linkDown",
	"linkUp",
	"authenticationFailure",
	"egpNeighborLoss",
	"enterpriseSpecific"
};

/*
 * ASN.1 type class table
 * Ties together the preceding Universal, Application, Context, and Private
 * type definitions.
 */
#define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */
static struct {
	char	*name;
	char	**Id;
	int	numIDs;
} Class[] = {
	defineCLASS(Universal),
#define	UNIVERSAL	0
	defineCLASS(Application),
#define	APPLICATION	1
	defineCLASS(Context),
#define	CONTEXT		2
	defineCLASS(Private),
#define	PRIVATE		3
};

/*
 * defined forms for ASN.1 types
 */
static char *Form[] = {
	"Primitive",
#define PRIMITIVE	0
	"Constructed",
#define CONSTRUCTED	1
};

/*
 * A tree in the format of the tree structure of the MIB.
 */
#define MAXLABEL 64	/* max label length */
#define MAXTOKEN 64
#define HASHSIZE 255
static struct tree {
    struct tree *child_list;    /* list of children of this node */
    struct tree *next_peer;     /* Next node in list of peers */
    struct tree *parent;
    struct tree *next_hash;     /* next hash entry.  used by parser */
    char label[MAXLABEL];       /* This node's textual name */
    u_long subid;               /* This node's integer subidentifier */
    int type;                   /* This node's object type */
};
static struct tree *Mib=NULL;
static struct tree *objp=NULL;
static void init_mib();
static struct tree *read_mib();
static struct tree *enter();
static struct tree *lookup();
static int add_child();

/*
 * This defines a list of OIDs which will be abreviated on output.
 * Currently, this includes the prefixes for the Internet MIB, the
 * private enterprises tree, and the experimental tree.
 */
static struct obj_abrev {
	char *prefix;			/* prefix for this abrev */
	struct tree *node;		/* pointer into object table */
	char *name;
	char *oid;			/* ASN.1 encoded OID */
} obj_abrev_list[] = {
	/* .iso.org.dod.internet.mgmt.mib */
	{ "", 0, "mib", "\53\6\1\2\1" },
	/* .iso.org.dod.internet.private.enterprises */
	{ "E:",	0, "enterprises",  "\53\6\1\4\1" },
	/* .iso.org.dod.internet.experimental */
	{ "X:",	0, "experimental", "\53\6\1\3" },
	{ 0,0,0,0 }
};

/*
 * This is used in the OID print routine to walk down the object tree
 * rooted at `Mib'.
 */
#define OBJ_PRINT(o, suppressdot) \
{ \
	if (objp) { \
		do { \
			if ((o) == objp->subid) \
				break; \
		} while (objp = objp->next_peer); \
	} \
	if (objp) { \
		printf(suppressdot?"%s":".%s", objp->label); \
		objp = objp->child_list; \
	} else \
		printf(suppressdot?"%u":".%u", (o)); \
}

/*
 * Defaults for SNMP PDU components
 */
#define DEF_COMMUNITY "public"
#define DEF_VERSION 0

/*
 * constants for ASN.1 decoding
 */
#define OIDMUX 40
#define ASNLEN_INETADDR 4
#define ASN_SHIFT7 7
#define ASN_SHIFT8 8
#define ASN_BIT8 0x80
#define ASN_LONGLEN 0x80

#define ASN_ID_BITS 0x1f
#define ASN_FORM_BITS 0x20
#define ASN_FORM_SHIFT 5
#define ASN_CLASS_BITS 0xc0
#define ASN_CLASS_SHIFT 6

#define ASN_ID_EXT 0x1f		/* extension ID in tag field */

#ifdef TCPVIEW
#define err_print eprint
#else
#define err_print printf
#endif

static char errbuf[10];

char *DECODE_ErrorStatus( e )
     int e;
{
  if( e >= 0 && e <= sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) )
    return( ErrorStatus[e] );
  sprintf(errbuf,"err=%d",e);
    return( errbuf );
}

char *DECODE_GenericTrap( e )
     int e;
{
  if( e >= 0 && e <= sizeof(GenericTrap)/sizeof(GenericTrap[0]) )
    return( GenericTrap[e] );
  sprintf(errbuf,"gt=%d",e);
    return( errbuf );
}
     
/*
 * truncated==1 means the packet was complete, but we don't have all of
 * it to decode.
 */
static int truncated;
#define ifNotTruncated if (truncated) fputs("[|snmp]", stdout); else

/*
 * This decodes the next ASN.1 object in the stream pointed to by "p"
 * (and of real-length "len") and stores the intermediate data in the
 * provided BE object.
 *
 * This returns -l if it fails (i.e., the ASN.1 stream is not valid).
 * O/w, this returns the number of bytes parsed from "p".
 */
int
asn1_parse(p, len, elem)
	register u_char *p;
	int len;
	struct be *elem;
{
	unsigned char form, class, id;
	int indent=0, i, hdr;
	char *classstr;

	elem->asnlen = 0;
	elem->type = BE_ANY;
	if (len < 1) {
		ifNotTruncated puts("[nothing to parse], stdout");
		return -1;
	}

	/*
	 * it would be nice to use a bit field, but you can't depend on them.
	 *  +---+---+---+---+---+---+---+---+
	 *  + class |frm|        id         |
	 *  +---+---+---+---+---+---+---+---+
	 *    7   6   5   4   3   2   1   0
	 */
	id = *p & ASN_ID_BITS;		/* lower 5 bits, range 00-1f */
#ifdef notdef
	form = (*p & 0xe0) >> 5;	/* move upper 3 bits to lower 3 */
	class = form >> 1;		/* bits 7&6 -> bits 1&0, range 0-3 */
	form &= 0x1;			/* bit 5 -> bit 0, range 0-1 */
#else
	form = (*p & ASN_FORM_BITS) >> ASN_FORM_SHIFT;
	class = (*p & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT;
#endif
	elem->form = form;
	elem->class = class;
	elem->id = id;
	if (vflag)
		printf("|%.2x", *p);
	p++; len--; hdr = 1;
	/* extended tag field */
	if (id == ASN_ID_EXT) {
		for (id = 0; *p & ASN_BIT8 && len > 0; len--, hdr++, p++) {
			if (vflag)
				printf("|%.2x", *p);
			id += *p & ~ASN_BIT8;
		}
		if (len == 0 && *p & ASN_BIT8) {
			ifNotTruncated fputs("[Xtagfield?]", stdout);
			return -1;
		}
	}
	if (len < 1) {
		ifNotTruncated fputs("[no asnlen]", stdout);
		return -1;
	}
	elem->asnlen = *p;
	if (vflag)
		printf("|%.2x", *p);
	p++; len--; hdr++;
	if (elem->asnlen & ASN_BIT8) {
		int noct = elem->asnlen % ASN_BIT8;
		elem->asnlen = 0;
		if (len < noct) {
			ifNotTruncated printf("[asnlen? %d<%d]", len, noct);
			return -1;
		}
		for (; noct-- > 0; len--, hdr++) {
			if (vflag)
				printf("|%.2x", *p);
			elem->asnlen = (elem->asnlen << ASN_SHIFT8) | *p++;
		}
	}
	if (len < elem->asnlen) {
		if (!truncated) {
			printf("[len%d<asnlen%u]", len, elem->asnlen);
			return -1;
		}
		/* maybe should check at least 4? */
		elem->asnlen = len;
	}
	if (form >= sizeof(Form)/sizeof(Form[0])) {
		ifNotTruncated printf("[form?%d]", form);
		return -1;
	}
	if (class >= sizeof(Class)/sizeof(Class[0])) {
		ifNotTruncated printf("[class?%c/%d]", *Form[form], class);
		return -1;
	}
	if (id >= Class[class].numIDs) {
		ifNotTruncated printf("[id?%c/%s/%d]", *Form[form],
			Class[class].name, id);
		return -1;
	}

	switch (form) {
	case PRIMITIVE:
		switch (class) {
		case UNIVERSAL:
			switch (id) {
			case STRING:
				elem->type = BE_STR;
				elem->data.str = p;
				break;

			case INTEGER: {
				register long data;
				elem->type = BE_INT;
				data = 0;

				if (*p & ASN_BIT8)	/* negative */
					data = -1;
				for (i = elem->asnlen; i-- > 0; p++)
					data = (data << ASN_SHIFT8) | *p;
				elem->data.integer = data;
				break;
			}

			case OBJECTID:
				elem->type = BE_OID;
				elem->data.raw = (caddr_t)p;
				break;

			case ASN_NULL:
				elem->type = BE_NULL;
				elem->data.raw = NULL;
				break;

			default:
				elem->type = BE_OCTET;
				elem->data.raw = (caddr_t)p;
				printf("[P/U/%s]",
					Class[class].Id[id]);
				break;
			}
			break;

		case APPLICATION:
			switch (id) {
			case IPADDR:
				elem->type = BE_INETADDR;
				elem->data.raw = (caddr_t)p;
				break;

			case COUNTER:
			case GAUGE:
			case TIMETICKS: {
				register unsigned long data;
				elem->type = BE_UNS;
				data = 0;
				for (i = elem->asnlen; i-- > 0; p++)
					data = (data << 8) + *p;
				elem->data.uns = data;
				break;
			}

			default:
				elem->type = BE_OCTET;
				elem->data.raw = (caddr_t)p;
				printf("[P/A/%s]",
					Class[class].Id[id]);
				break;
			}
			break;

		default:
			elem->type = BE_OCTET;
			elem->data.raw = (caddr_t)p;
			printf("[P/%s/%s]",
				Class[class].name, Class[class].Id[id]);
			break;
		}
		break;

	case CONSTRUCTED:
		switch (class) {
		case UNIVERSAL:
			switch (id) {
			case SEQUENCE:
				elem->type = BE_SEQ;
				elem->data.raw = (caddr_t)p;
				break;

			default:
				elem->type = BE_OCTET;
				elem->data.raw = (caddr_t)p;
				printf("C/U/%s", Class[class].Id[id]);
				break;
			}
			break;

		case CONTEXT:
			elem->type = BE_PDU;
			elem->data.raw = (caddr_t)p;
			break;

		default:
			elem->type = BE_OCTET;
			elem->data.raw = (caddr_t)p;
			printf("C/%s/%s",
				Class[class].name, Class[class].Id[id]);
			break;
		}
		break;
	}
	p += elem->asnlen;
	len -= elem->asnlen;
	return elem->asnlen + hdr;
}

/*
 * Display the ASN.1 object represented by the BE object.
 */
void asn1_print(elem)
     struct be *elem;
{
  u_char *p = (u_char *)elem->data.raw;
  u_long asnlen = elem->asnlen;
  int i;
  
  switch (elem->type) {
    
  case BE_OCTET:
    for (i = asnlen; i-- > 0; p++);
    printf("_%.2x", *p);
    break;
    
  case BE_NULL:
    break;
    
  case BE_OID: {
    int o = 0, first = -1, i = asnlen;
    
    if (!nflag && asnlen > 2) {
      struct obj_abrev *a = &obj_abrev_list[0];
      for (; a->node; a++) {
	if (!memcmp(a->oid, p, strlen(a->oid))) {
	  objp = a->node->child_list;
	  i -= strlen(a->oid);
	  p += strlen(a->oid);
	  fputs(a->prefix, stdout);
	  first = 1;
	  break;
	}
      }
    }

    for (; i-- > 0; p++) {
      o = (o << ASN_SHIFT7) + (*p & ~ASN_BIT8);
      if (*p & ASN_LONGLEN)
	continue;
      
      /*
       * first subitem encodes two items with 1st*OIDMUX+2nd
       */
      if (first < 0) {
	if (!nflag)
	  objp = Mib;
	first = 0;
	OBJ_PRINT(o/OIDMUX, first);
	o %= OIDMUX;
      }
      OBJ_PRINT(o, first);
      if (--first < 0)
	first = 0;
      o = 0;
    }
    break;
  }
    
  case BE_INT:
    printf("%ld", elem->data.integer);
    break;
    
  case BE_UNS:
    printf("%lu", elem->data.uns);
    break;
    
  case BE_STR: {
    register int printable = 1, first = 1;
    u_char *p = elem->data.str;
    for (i = asnlen; printable && i-- > 0; p++)
      printable = isprint(*p) || isspace(*p);
    p = elem->data.str;
    if (printable)
      (void)printfn(p, p+asnlen);
    else
      for (i = asnlen; i-- > 0; p++) {
	printf(first ? "%.2x" : "_%.2x", *p);
	first = 0;
      }
    break;
  }
    
  case BE_SEQ:
    printf("Seq(%d)", elem->asnlen);
    break;
    
  case BE_INETADDR: {
    char sep;
    if (asnlen != ASNLEN_INETADDR)
      printf("[inetaddr len!=%d]", ASNLEN_INETADDR);
    sep='[';
    for (i = asnlen; i-- > 0; p++) {
      printf("%c%u", sep, *p);
      sep='.';
    }
    putchar(']');
    break;
  }
    
  case BE_PDU:
    printf("%s(%d)",
	   Class[CONTEXT].Id[elem->id], elem->asnlen);
    break;
    
  case BE_ANY:
    fputs("[BE_ANY!?]", stdout);
    break;
    
  default:
    fputs("[be!?]", stdout);
    break;
  }
}

#ifdef notdef
/*
 * This is a brute force ASN.1 printer: recurses to dump an entire structure.
 * This will work for any ASN.1 stream, not just an SNMP PDU.
 *
 * By adding newlines and spaces at the correct places, this would print in
 * Rose-Normal-Form.
 *
 * This is not currently used.
 */
void
asn1_decode(p, length)
	u_char *p;
	int length;
{
	struct be elem;
	int i = 0;

	while (i >= 0 && length > 0) {
		i = asn1_parse(p, length, &elem);
		if (i >= 0) {
			fputs(" ", stdout);
			asn1_print(&elem);
			if (elem.type == BE_SEQ || elem.type == BE_PDU) {
				fputs(" {", stdout);
				asn1_decode(elem.data.raw, elem.asnlen);
				fputs(" }", stdout);
			}
			length -= i;
			p += i;
		}
	}
}
#endif

/*
 * General SNMP header
 *	SEQUENCE {
 *		version INTEGER {version-1(0)},
 *		community OCTET STRING,
 *		data ANY 	-- PDUs
 *	}
 * PDUs for all but Trap: (see rfc1157 from page 15 on)
 *	SEQUENCE {
 *		request-id INTEGER,
 *		error-status INTEGER,
 *		error-index INTEGER,
 *		varbindlist SEQUENCE OF
 *			SEQUENCE {
 *				name ObjectName,
 *				value ObjectValue
 *			}
 *	}
 * PDU for Trap:
 *	SEQUENCE {
 *		enterprise OBJECT IDENTIFIER,
 *		agent-addr NetworkAddress,
 *		generic-trap INTEGER,
 *		specific-trap INTEGER,
 *		time-stamp TimeTicks,
 *		varbindlist SEQUENCE OF
 *			SEQUENCE {
 *				name ObjectName,
 *				value ObjectValue
 *			}
 *	}
 */

/*
 * Decode SNMP varBind
 */
static void
varbind_print (pduid, np, length, error)
	u_char pduid, *np;
	int length, error;
{
	struct be elem;
	int count = 0, index;

	/* Sequence of varBind */
	if ((count = asn1_parse(np, length, &elem)) < 0)
		return;
	if (elem.type != BE_SEQ) {
		fputs("[!SEQ of varbind]", stdout);
		asn1_print(&elem);
		return;
	}
	if (count < length)
		printf("[%d extra after SEQ of varbind]", length - count);
	/* descend */
	length = elem.asnlen;
	np = (u_char *)elem.data.raw;

	for (index = 1; length > 0; index++) {
		u_char *vbend;
		int vblength;

		if (!error || index == error)
			fputs(" ", stdout);

		/* Sequence */
		if ((count = asn1_parse(np, length, &elem)) < 0)
			return;
		if (elem.type != BE_SEQ) {
			fputs("[!varbind]", stdout);
			asn1_print(&elem);
			return;
		}
		vbend = np + count;
		vblength = length - count;
		/* descend */
		length = elem.asnlen;
		np = (u_char *)elem.data.raw;

		/* objName (OID) */
		if ((count = asn1_parse(np, length, &elem)) < 0)
			return;
		if (elem.type != BE_OID) {
			fputs("[objName!=OID]", stdout);
			asn1_print(&elem);
			return;
		}
		if (!error || index == error)
			asn1_print(&elem);
		length -= count;
		np += count;

		if (pduid != GETREQ && pduid != GETNEXTREQ && !error)
				fputs("=", stdout);

		/* objVal (ANY) */
		if ((count = asn1_parse(np, length, &elem)) < 0)
			return;
		if (pduid == GETREQ || pduid == GETNEXTREQ) {
			if (elem.type != BE_NULL) {
				fputs("[objVal!=NULL]", stdout);
				asn1_print(&elem);
			}
		} else
			if (error && index == error && elem.type != BE_NULL)
				fputs("[err objVal!=NULL]", stdout);
			if (!error || index == error)
				asn1_print(&elem);

		length = vblength;
		np = vbend;
	}
}

/*
 * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, and SetRequest
 */
static void
snmppdu_print (pduid, np, length)
	u_char pduid, *np;
	int length;
{
	struct be elem;
	int count = 0, error;

	/* reqId (Integer) */
	if ((count = asn1_parse(np, length, &elem)) < 0)
		return;
	if (elem.type != BE_INT) {
		fputs("[reqId!=INT]", stdout);
		asn1_print(&elem);
		return;
	}
	/* ignore the reqId */
	length -= count;
	np += count;

	/* errorStatus (Integer) */
	if ((count = asn1_parse(np, length, &elem)) < 0)
		return;
	if (elem.type != BE_INT) {
		fputs("[errorStatus!=INT]", stdout);
		asn1_print(&elem);
		return;
	}
	error = 0;
	if ((pduid == GETREQ || pduid == GETNEXTREQ)
	    && elem.data.integer != 0) {
		printf("[errorStatus(%s)!=0]", 
			DECODE_ErrorStatus(elem.data.integer));
	} else if (elem.data.integer != 0) {
		printf(" %s", DECODE_ErrorStatus(elem.data.integer));
		error = elem.data.integer;
	}
	length -= count;
	np += count;

	/* errorIndex (Integer) */
	if ((count = asn1_parse(np, length, &elem)) < 0)
		return;
	if (elem.type != BE_INT) {
		fputs("[errorIndex!=INT]", stdout);
		asn1_print(&elem);
		return;
	}
	if ((pduid == GETREQ || pduid == GETNEXTREQ)
	    && elem.data.integer != 0)
		printf("[errorIndex(%d)!=0]", elem.data.integer);
	else if (elem.data.integer != 0) {
		if (!error)
			printf("[errorIndex(%d) w/o errorStatus]",
				elem.data.integer);
		else {
			printf("@%d", elem.data.integer);
			error = elem.data.integer;
		}
	} else if (error) {
		fputs("[errorIndex==0]", stdout);
		error = 0;
	}
	length -= count;
	np += count;

	varbind_print(pduid, np, length, error);
	return;
}

/*
 * Decode SNMP Trap PDU
 */
static void
trap_print (np, length)
	u_char *np;
	int length;
{
	struct be elem;
	int count = 0, generic;

	putchar(' ');

	/* enterprise (oid) */
	if ((count = asn1_parse(np, length, &elem)) < 0)
		return;
	if (elem.type != BE_OID) {
		fputs("[enterprise!=OID]", stdout);
		asn1_print(&elem);
		return;
	}
	asn1_print(&elem);
	length -= count;
	np += count;

	putchar(' ');

	/* agent-addr (inetaddr) */
	if ((count = asn1_parse(np, length, &elem)) < 0)
		return;
	if (elem.type != BE_INETADDR) {
		fputs("[agent-addr!=INETADDR]", stdout);
		asn1_print(&elem);
		return;
	}
	asn1_print(&elem);
	length -= count;
	np += count;

	/* generic-trap (Integer) */
	if ((count = asn1_parse(np, length, &elem)) < 0)
		return;
	if (elem.type != BE_INT) {
		fputs("[generic-trap!=INT]", stdout);
		asn1_print(&elem);
		return;
	}
	generic = elem.data.integer;
	printf(" %s", DECODE_GenericTrap(generic));

	length -= count;
	np += count;

	/* specific-trap (Integer) */
	if ((count = asn1_parse(np, length, &elem)) < 0)
		return;
	if (elem.type != BE_INT) {
		fputs("[specific-trap!=INT]", stdout);
		asn1_print(&elem);
		return;
	}
	if (generic != GT_ENTERPRISE) {
		if (elem.data.integer != 0)
			printf("[specific-trap(%d)!=0]", elem.data.integer);
	} else
		printf(" s=%d", elem.data.integer);
	length -= count;
	np += count;

	putchar(' ');

	/* time-stamp (TimeTicks) */
	if ((count = asn1_parse(np, length, &elem)) < 0)
		return;
	if (elem.type != BE_UNS) {			/* XXX */
		fputs("[time-stamp!=TIMETICKS]", stdout);
		asn1_print(&elem);
		return;
	}
	asn1_print(&elem);
	length -= count;
	np += count;

	varbind_print (TRAP, np, length, 0);
	return;
}

/*
 * Decode SNMP header and pass on to PDU printing routines
 */
void
snmp_print (np, length)
	u_char *np;
	int length;
{
	struct be elem, pdu;
	int count = 0;

	if( Mib == NULL )
	  init_mib();

	truncated = 0;

	/* truncated packet? */
	if (np + length > snapend) {
		truncated = 1;
		length = snapend - np;
	}

	putchar(' ');

	/* initial Sequence */
	if ((count = asn1_parse(np, length, &elem)) < 0)
		return;
	if (elem.type != BE_SEQ) {
		fputs("[!init SEQ]", stdout);
		asn1_print(&elem);
		return;
	}
	if (count < length)
		printf("[%d extra after iSEQ]", length - count);
	/* descend */
	length = elem.asnlen;
	np = (u_char *)elem.data.raw;
	/* Version (Integer) */
	if ((count = asn1_parse(np, length, &elem)) < 0)
		return;
	if (elem.type != BE_INT) {
		fputs("[version!=INT]", stdout);
		asn1_print(&elem);
		return;
	}
	/* only handle version==0 */
	if (elem.data.integer != DEF_VERSION) {
		printf("[version(%d)!=0]", elem.data.integer);
		return;
	}
	length -= count;
	np += count;

	/* Community (String) */
	if ((count = asn1_parse(np, length, &elem)) < 0)
		return;
	if (elem.type != BE_STR) {
		fputs("[comm!=STR]", stdout);
		asn1_print(&elem);
		return;
	}
	/* default community */
	if (strncmp(elem.data.str, DEF_COMMUNITY, sizeof(DEF_COMMUNITY)-1))
		/* ! "public" */
		printf("C=%.*s ", elem.asnlen, elem.data.str);
	length -= count;
	np += count;

	/* PDU (Context) */
	if ((count = asn1_parse(np, length, &pdu)) < 0)
		return;
	if (pdu.type != BE_PDU) {
		fputs("[no PDU]", stdout);
		return;
	}
	if (count < length)
		printf("[%d extra after PDU]", length - count);
	asn1_print(&pdu);
	/* descend into PDU */
	length = pdu.asnlen;
	np = (u_char *)pdu.data.raw;

	switch (pdu.id) {
	case TRAP:
		trap_print(np, length);
		break;
	case GETREQ:
	case GETNEXTREQ:
	case GETRESP:
	case SETREQ:
		snmppdu_print(pdu.id, np, length);
		break;
	}
	return;
}

static void init_mib()
{
  char *file, *getenv();
  struct obj_abrev *a = &obj_abrev_list[0];

  Mib = 0;
  file = getenv("MIBFILE");
  if (file)
    Mib = read_mib(file);
  if (!Mib)
    Mib = read_mib("mib.txt");
  if (!Mib)
    Mib = read_mib("/usr/local/lib/tcpview/mib/mib.txt");
  if (!Mib){
    err_print("Couldn't find mib file\n");
    exit(2);
  }

  for (; a->name; a++) {
    a->node = lookup(a->name);
    if( a->node  == NULL ) 
      err_print("WARNING:  Could not find %s in MIB.\n",a->name);
  }
}

static struct tree *Hashtable[HASHSIZE];

static struct tree *read_mib(filename)
     char *filename;
{
  FILE *fp;
  struct tree *tp;
  struct tree *parent, *hp, *iso;
  char buf[256], name[64], *ptr, *stp;
  int type=0, access=0;

  fp = fopen(filename, "r");
  if (fp == NULL)
    return NULL;

  /* create iso */
  iso = enter("iso");
  iso->subid = 1;

  /* read in mosy file */
  while(fgets(buf,sizeof(buf),fp)) {
    if(*buf == '#' || isspace(*buf) || (buf[0]=='-' && buf[1]=='-'))
      continue;
    ptr = buf;
    while(!isspace(*ptr))
      ptr++;
    *ptr++ = 0;
    strncpy(name,buf,sizeof(name));
    name[sizeof(name)-1]='\0';
    
    while(isspace(*ptr)) ptr++;
    stp = ptr;
    while(*ptr != '.')
      ptr++;
    *ptr++ = 0;
    parent = lookup(stp);
    if(parent==NULL) {
      err_print("Couldn't find parent %s of %s\n",stp,name);
      exit(0);
    }
    stp = ptr;
    while(!isspace(*ptr)) ptr++;
    *ptr++ = 0;

    hp = lookup(name);
    if(hp==NULL)
      hp = enter(name);
    else {  /* name already exists */
      if(hp->parent==parent)
        if(hp->subid==atoi(stp))
          continue;    /* duplicate definition - skip it */
        else {
          err_print("Error: %s redefined\n",name);
          exit(0);
        }
      else
        hp = enter(name);
    }
    hp->subid = atoi(stp);
    add_child(parent,hp);
    while(isspace(*ptr)) ptr++;
    if(*ptr==0)
      continue;
    stp = ptr;
    while(!isspace(*ptr)) ptr++;
    *ptr++ = 0;
    
    if(!strcmp(stp,"INTEGER"))
      type = BE_INT;
    else if(!strcmp(stp,"Counter"))
      type = BE_UNS;
    else if(!strcmp(stp,"DisplayString"))
      type = BE_STR;
    else if(!strcmp(stp,"Gauge"))
      type = BE_UNS;
    else if(!strcmp(stp,"TimeTicks"))
      type = BE_UNS;
    else if(!strcmp(stp,"NetworkAddress"))
      type = BE_INETADDR;
    else if(!strcmp(stp,"IpAddress"))
      type = BE_INETADDR;
    else if(!strcmp(stp,"OctetString"))
      type = BE_OCTET;
    else if(!strcmp(stp,"ObjectID"))
      type = BE_OID;
    else if(!strcmp(stp,"Aggregate"))
      type = BE_ANY;
    else if(!strcmp(stp,"PhysAddress"))
      type = BE_OCTET;
    else {
      err_print("type %s not recognized\n",stp);
      type = BE_ANY;
    }
    hp->type = type;
  }
  fclose(fp);
  return (iso);
}

/*
** lookup(name) returns a pointer to "name" in the hash table.
** returns NULL if the name is not found
*/

static struct tree *lookup(name)
     char *name;
{
  struct tree *hp;
  int hash;

  hash = (name[0]+name[1]) & HASHSIZE;
  hp = Hashtable[hash];
  while(hp) {
    if (!strcmp(hp->label,name))
      return (hp);
    hp = hp->next_hash;
  }
  return(NULL);
}

/*
** enter() enters a new name in the hash table.
** New entries are put at the beginning of the hash chain.
*/
static struct tree *enter(name)
     char *name;
{
  struct tree *hp;
  int hash;

  hash = (name[0]+name[1]) & HASHSIZE;
  hp = (struct tree *)malloc(sizeof(struct tree));
  hp->next_hash = Hashtable[hash];
  Hashtable[hash] = hp;
  strcpy(hp->label,name);
  hp->child_list = hp->next_peer = hp->parent = 0;
  hp->type = BE_NONE;
  return(hp);

}

/* add a child to a node in the tree */
static int add_child(parent, child)
     struct tree *parent, *child;
{
  struct tree *t = parent->child_list;

/*  fprintf(stderr,"adding %s to %s (%d)\n",child->label,parent->label,child->subid); */
  if(t==NULL) {
    parent->child_list = child;
    child->next_peer = 0;
    child->parent = parent;
    return 1;
  }

  while(1) {
    if(t->subid==child->subid) {
      if(!strcmp(t->label,child->label)) {
        return 0;
      } else {
        err_print("Warning: node %s has 2 children with subid %d!\n",parent->label,child->subid);
        return 0;
      }
    }
    if(t->next_peer)
      t = t->next_peer;
    else
      break;
  }

  t->next_peer = child;
  child->next_peer = 0;
  child->parent = parent;

  return 1;
}
 




syntax highlighted by Code2HTML, v. 0.9.1