/*
 * 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: detail-snmp.c,v 1.1 1993/04/22 20:17:17 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"

/*
 * truncated==1 means the packet was complete, but we don't have all of
 * it to decode.
 */
static int truncated;

/*
 * Decode SNMP varBind
 */
static void varbind_print (pduid, np, length, err)
     u_char pduid, *np;
     int length, err;
{
  struct be elem;
  int count = 0, index;
  
  /* Sequence of varBind */
  if ((count = asn1_parse(np, length, &elem)) < 0)
    return;
  hex(0,count-1);
  if (elem.type != BE_SEQ) {
    printf("ERROR: expected Sequence of variables\n");
    return;
  }
  printf("Sequence of VarBind (%d bytes)",count);
  if (count < length)
    printf("  [%d extra after Sequence]", length - count);
  putchar('\n');

  /* descend */
  length = elem.asnlen;
  Offset += (u_long)elem.data.raw - (u_long)np;
  np = (u_char *)elem.data.raw;
  
  for (index = 1; length > 0; index++) {
    u_char *vbend;
    int vblength;

    /* Sequence (VarBind) */
    if ((count = asn1_parse(np, length, &elem)) < 0)
      return;
    hex(0,count-1);
    if (elem.type != BE_SEQ) {
      printf("  ERROR: expected VarBind\n");
      return;
    }
    vbend = np + count;
    vblength = length - count;
    printf("  VarBind (%d bytes)\n",elem.asnlen);

    /* descend */
    length = elem.asnlen;
    Offset += (u_long)elem.data.raw - (u_long)np;
    np = (u_char *)elem.data.raw;
    
    /* objName (OID) */
    if ((count = asn1_parse(np, length, &elem)) < 0)
      return;
    hex(0,count-1);
    if (elem.type != BE_OID) {
      printf("    ERROR: expected OBJECT ID\n");
      return;
    }

    if (err && index == err)
      printf("UNKNOWN ");
    printf("    OBJECT: ");
    asn1_print(&elem);

    putchar('\n');
    length -= count;
    np += count;
    Offset += count;
    

    /* objVal (ANY) */
    if ((count = asn1_parse(np, length, &elem)) < 0)
      return;
    hex(0,count-1);
    printf("    VALUE: ");
    if( elem.type == BE_NULL )
      printf("NULL");
    else
      asn1_print(&elem);
    putchar('\n');
    length = vblength;
    np = vbend;
    Offset += count;
  }
}

/*
 * 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, index;
  
  /* reqId (Integer) */
  if ((count = asn1_parse(np, length, &elem)) < 0)
    return;
  hex(0,count-1);
  if (elem.type != BE_INT) {
    printf("ERROR:  expected INTEGER\n");
    return;
  }
  printf("Request ID: %d\n",elem.data.integer);

  length -= count;
  np += count;
  Offset += count;

  /* errorStatus (Integer) */
  if ((count = asn1_parse(np, length, &elem)) < 0)
    return;
  hex(0,count-1);
  if (elem.type != BE_INT) {
    printf("ERROR:  expected INTEGER\n");
    return;
  }

  error = elem.data.integer;
  printf("Error Status = %d (%s)\n", error, DECODE_ErrorStatus(error));
  
  length -= count;
  np += count;
  Offset += count;

  /* errorIndex (Integer) */
  if ((count = asn1_parse(np, length, &elem)) < 0)
    return;
  hex(0,count-1);
  if (elem.type != BE_INT) {
    printf("ERROR: expected INTEGER\n");
    return;
  }
  printf("Error Index = %d\n", elem.data.integer);

  length -= count;
  np += count;
  Offset += count;
  
  if( error )
    index = elem.data.integer;
  else
    index = 0;

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

/*
 * Decode SNMP Trap PDU
 */
static void trap_print (np, length)
     u_char *np;
     int length;
{
  struct be elem;
  int count = 0, generic;
  
  /* enterprise (oid) */
  if ((count = asn1_parse(np, length, &elem)) < 0)
    return;
  hex(0,count-1);
  if (elem.type != BE_OID) {
    printf("ERROR:  Expected enterprise OID\n");
    return;
  }
  printf("Enterprise = ");
  asn1_print(&elem);
  putchar('\n');

  length -= count;
  np += count;
  Offset += count;
  
  /* agent-addr (inetaddr) */
  if ((count = asn1_parse(np, length, &elem)) < 0)
    return;
  hex(0,count-1);
  if (elem.type != BE_INETADDR) {
    printf("ERROR:  expected NetworkAddress\n");
    return;
  }
  printf("Agent Address = ");
  asn1_print(&elem);
  putchar('\n');
  length -= count;
  np += count;
  Offset += count;
  
  /* generic-trap (Integer) */
  if ((count = asn1_parse(np, length, &elem)) < 0)
    return;
  hex(0,count-1);
  if (elem.type != BE_INT) {
    printf("ERROR:  expected INTEGER\n");
    return;
  }
  generic = elem.data.integer;
  printf("Generic Trap Type = %d (%s)\n", generic, DECODE_GenericTrap(generic));
  length -= count;
  np += count;
  Offset += count;
  
  /* specific-trap (Integer) */
  if ((count = asn1_parse(np, length, &elem)) < 0)
    return;
  hex(0,count-1);
  if (elem.type != BE_INT) {
    printf("ERROR:  expected INTEGER\n");
    return;
  }
  printf("Specific Trap = %d\n", elem.data.integer);
  length -= count;
  np += count;
  Offset += count;
  
  /* time-stamp (TimeTicks) */
  if ((count = asn1_parse(np, length, &elem)) < 0)
    return;
  hex(0,count-1);
  if (elem.type != BE_UNS) {			/* XXX */
    printf("ERROR:  expected TimeTicks\n");
    return;
  }
  printf("TimeStamp = ");
  asn1_print(&elem);
  putchar('\n');
  length -= count;
  np += count;
  Offset += count;
  
  varbind_print (TRAP, np, length, 0);
  return;
}

/*
 * Decode SNMP header and pass on to PDU printing routines
 */
void detail_snmp (np, length)
     u_char *np;
     int length;
{
  struct be elem, pdu;
  int count = 0;
  
  truncated = 0;
  
  /* truncated packet? */
  if (length > Phdr->caplen-Offset) {
    truncated = 1;
    length = Phdr->caplen-Offset;
    printf("*** SNMP (truncated) ***\n\n");
  } else
    printf("----- SNMP -----\n\n");
  hex( 0, length-1 );
  hex( -1, -1 );                         /* blank line */

  /* initial Sequence */
  if ((count = asn1_parse(np, length, &elem)) < 0)
    return;
  if (elem.type != BE_SEQ) {
    printf("ERROR:  expected SEQUENCE\n");
    hex(0,count-1);
    return;
  }
  printf("Message Sequence (%d bytes)",count);
  if (count < length)
    printf(" [%d extra after SEQ]", length - count);
  hex(0,count-1);
  putchar('\n');

  /* descend */
  Offset += ((u_long)elem.data.raw - (u_long)np);
  length = elem.asnlen;
  np = (u_char *)elem.data.raw;

  /* Version (Integer) */
  if ((count = asn1_parse(np, length, &elem)) < 0)
    return;
  hex(0,count-1);
  if (elem.type != BE_INT) {
    printf("ERROR: expected INTEGER\n");
    return;
  }
  /* only handle version==0 */
  printf("Version: %d",elem.data.integer);
  if (elem.data.integer != DEF_VERSION) {
    printf("\t[should have been 0]");
    return;
  }
  putchar('\n');

  length -= count;
  np += count;
  Offset += count;

  /* Community (String) */
  if ((count = asn1_parse(np, length, &elem)) < 0)
    return;
  hex(0,count-1);
  if (elem.type != BE_STR) {
    printf("ERROR: expected STRING\n");
    return;
  }
  /* default community */
  printf("Community = \"%.*s\"\n", elem.asnlen,elem.data.str);
  length -= count;
  np += count;
  Offset += count;


  /* PDU (Context) */
  if ((count = asn1_parse(np, length, &pdu)) < 0)
    return;
  hex(0,count-1);
  if (pdu.type != BE_PDU) {
    printf("ERROR: expected PDU\n");
    return;
  }
  asn1_print(&pdu);
  if (count < length)
    printf("  [%d extra after PDU]", length - count);
  putchar('\n');

  /* descend into PDU */

  length = pdu.asnlen;
  Offset += (u_long)pdu.data.raw -(u_long)np;
  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;
}


syntax highlighted by Code2HTML, v. 0.9.1