/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * GXSNMP -- An snmp mangament application
 * Copyright (C) 1998 Gregory McLean & Jochen Friedrich
 * Beholder RMON ethernet network monitor, Copyright (C) 1993 DNPAP group
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/*
 * MODULE INFORMATION 
 * ------------------ 
 *     FILE     NAME:       g_snmp.c
 *     SYSTEM   NAME:       SNMP Packet Module
 *     ORIGINAL AUTHOR(S):  Dirk Wisse
 *     VERSION  NUMBER:     1
 *     CREATION DATE:       1990/11/28
 *
 * DESCRIPTION: SNMP Packet Module
 *
 */

#include "g_snmp.h"

/*prototypes*/
static gboolean g_snmp_syntax2tag_cls(guint *tag, guint *cls, gint syntax);
static gboolean g_snmp_tag_cls2syntax(guint tag, guint cls, gushort *syntax);
static gboolean g_snmp_object_encode(ASN1_SCK *asn1, SNMP_OBJECT *object);
static gboolean g_snmp_object_decode(ASN1_SCK *asn1, SNMP_OBJECT **object);
static gboolean g_snmp_list_encode(ASN1_SCK *asn1, GSList *list);
static gboolean g_snmp_list_decode(ASN1_SCK *asn1, GSList **list);
static gboolean g_snmp_request_encode(ASN1_SCK *asn1, SNMP_REQUEST *request);
static gboolean g_snmp_request_decode(ASN1_SCK *asn1, SNMP_REQUEST *request);
static gboolean g_snmp_trap_encode(ASN1_SCK *asn1, SNMP_V1_TRAP *trap);
static gboolean g_snmp_trap_decode(ASN1_SCK *asn1, SNMP_V1_TRAP *trap);

#define SNMP_IPA    0
#define SNMP_CNT    1
#define SNMP_GGE    2
#define SNMP_TIT    3
#define SNMP_OPQ    4
#define SNMP_C64    6

#define SERR_NSO    0
#define SERR_NSI    1
#define SERR_EOM    2

guint g_snmpErrStatus = SNMP_NOERROR;
guint g_snmpErrIndex = 0;

typedef struct _SNMP_CNV SNMP_CNV;

struct _SNMP_CNV
{
  guint class;
  guint tag;
  gint  syntax;
};

const char *SnmpTrap[] =
{
  "cold start",
  "warm start",
  "link down",
  "link up",
  "authentication failure",
  "neighbor loss",
  "enterprise specific"
};

static SNMP_CNV SnmpCnv [] =
{
  {ASN1_UNI, ASN1_NUL, SNMP_NULL},
  {ASN1_UNI, ASN1_INT, SNMP_INTEGER},
  {ASN1_UNI, ASN1_OTS, SNMP_OCTETSTR},
  {ASN1_UNI, ASN1_OTS, SNMP_DISPLAYSTR},
  {ASN1_UNI, ASN1_OJI, SNMP_OBJECTID},
  {ASN1_APL, SNMP_IPA, SNMP_IPADDR},
  {ASN1_APL, SNMP_CNT, SNMP_COUNTER},           /* Counter32 */
  {ASN1_APL, SNMP_GGE, SNMP_GAUGE},             /* Gauge32 == Unsigned32  */
  {ASN1_APL, SNMP_TIT, SNMP_TIMETICKS},
  {ASN1_APL, SNMP_OPQ, SNMP_OPAQUE},

/* SNMPv2 data types and errors */

  {ASN1_UNI, ASN1_BTS, SNMP_BITSTR},
  {ASN1_APL, SNMP_C64, SNMP_COUNTER64},
  {ASN1_CTX, SERR_NSO, SNMP_NOSUCHOBJECT},
  {ASN1_CTX, SERR_NSI, SNMP_NOSUCHINSTANCE},
  {ASN1_CTX, SERR_EOM, SNMP_ENDOFMIBVIEW},
  {0,       0,       -1}
};

/*
 * NAME:        g_snmp_syntax2tag_cls
 * SYNOPSIS:    gboolean g_snmp_syntax2tag_cls
 *                  (
 *                      guint   *tag,
 *                      guint   *cls,
 *                      gint     syntax
 *                  )
 * DESCRIPTION: Converts Syntax tag to ASN1 tag and class.
 *              See SnmpCnv for conversion.
 * RETURNS:     gboolean success.
 */

static gboolean  
g_snmp_syntax2tag_cls ( guint *tag, guint *cls, gint syntax)
{
    SNMP_CNV *cnv;

    cnv = SnmpCnv;
    while (cnv->syntax != -1)
    {
        if (cnv->syntax == syntax)
        {
            *tag = cnv->tag;
            *cls = cnv->class;
            return TRUE;
        }
        cnv++;
    }
    g_snmpErrStatus = SNMP_BADVALUE;
    return FALSE;
}

/*
 * NAME:        g_snmp_tag_cls2syntax
 * SYNOPSIS:    gboolean g_snmp_tag_cls2syntax
 *                  (
 *                      guint    tag,
 *                      guint    cls,
 *                      gushort *syntax
 *                  )
 * DESCRIPTION: Converts ASN1 tag and class to Syntax tag.
 *              See SnmpCnv for conversion.
 * RETURNS:     gboolean success.
 */

static gboolean 
g_snmp_tag_cls2syntax ( guint tag, guint cls, gushort *syntax)
{
    SNMP_CNV *cnv;

    cnv = SnmpCnv;
    while (cnv->syntax != -1)
    {
        if (cnv->tag == tag && cnv->class == cls)
        {
            *syntax = cnv->syntax;
            return TRUE;
        }
        cnv++;
    }
    g_snmpErrStatus = SNMP_BADVALUE;
    return FALSE;
}

/*
 * NAME:        g_snmp_object_encode
 * SYNOPSIS:    gboolean g_snmp_object_encode
 *                  (
 *                      ASN1_SCK     *asn1,
 *                      SNMP_OBJECT  *obj,
 *                  )
 * DESCRIPTION: Encodes an object in ASN1.
 * RETURNS:     gboolean success.
 */

static gboolean 
g_snmp_object_encode ( ASN1_SCK *asn1, SNMP_OBJECT *obj)
{
    guint   cls, tag;
    guchar *eoc, *end;

    if (!g_asn1_eoc_encode (asn1, &eoc))
        return FALSE;
    switch (obj->type)
    {
        case SNMP_INTEGER:
            if (!g_asn1_long_encode (asn1, &end, obj->syntax.l[0]))
                return FALSE;
            break;
        case SNMP_OCTETSTR:
        case SNMP_OPAQUE:
            if (!g_asn1_octets_encode (asn1, &end, obj->syntax.c, 
                                       obj->syntax_len))
                return FALSE;
            break;
        case SNMP_NULL:
	case SNMP_NOSUCHOBJECT:
	case SNMP_NOSUCHINSTANCE:
	case SNMP_ENDOFMIBVIEW:
            if (!g_asn1_null_encode (asn1, &end))
                return FALSE;
            break;
        case SNMP_OBJECTID:
            if (!g_asn1_oid_encode (asn1, &end, obj->syntax.ul, 
                                    obj->syntax_len))
                return FALSE;
            break;
        case SNMP_IPADDR:
            if (!g_asn1_octets_encode (asn1, &end, obj->syntax.uc, 
                                       obj->syntax_len))
                return FALSE;
            break;
        case SNMP_COUNTER:
        case SNMP_GAUGE:
        case SNMP_TIMETICKS:
            if (!g_asn1_ulong_encode (asn1, &end, obj->syntax.ul[0]))
                return FALSE;
            break;
        default:
            g_snmpErrStatus = SNMP_BADVALUE;
            return FALSE;
    }
    if (!g_snmp_syntax2tag_cls (&tag, &cls, obj->type))
        return FALSE;
    if (!g_asn1_header_encode (asn1, end, cls, ASN1_PRI, tag))
        return FALSE;
    if (!g_asn1_oid_encode (asn1, &end, obj->id, obj->id_len))
        return FALSE;
    if (!g_asn1_header_encode (asn1, end, ASN1_UNI, ASN1_PRI, ASN1_OJI))
        return FALSE;
    if (!g_asn1_header_encode (asn1, eoc, ASN1_UNI, ASN1_CON, ASN1_SEQ))
        return FALSE;
    return TRUE;
}

/*
 * NAME:        g_snmp_object_decode
 * SYNOPSIS:    gboolean g_snmp_object_decode
 *                  (
 *                      ASN1_SCK     *asn1,
 *                      SNMP_OBJECT  *obj,
 *                  )
 * DESCRIPTION: Decodes an object from ASN1.
 * RETURNS:     gboolean success.
 */

static gboolean 
g_snmp_object_decode ( ASN1_SCK *asn1, SNMP_OBJECT **obj)
{
  guint cls, con, tag, len, idlen;
  gushort type;
  guchar *eoc, *end, *p;
  gulong *lp, *id;
  gulong ul;
  glong  l;

  *obj = NULL;
  if (!g_asn1_header_decode (asn1, &eoc, &cls, &con, &tag))
    return FALSE;
  if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ)
    return FALSE;
  if (!g_asn1_header_decode (asn1, &end, &cls, &con, &tag))
    return FALSE;
  if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_OJI)
    return FALSE;
  if (!g_asn1_oid_decode (asn1, end, &id, &idlen))
    return FALSE;
  if (!g_asn1_header_decode (asn1, &end, &cls, &con, &tag))
    {
      g_free(id);
      return FALSE;
    }
  if (con != ASN1_PRI)
    {
      g_snmpErrStatus = SNMP_BADVALUE;
      g_free(id);
      return FALSE;
    }
  if (!g_snmp_tag_cls2syntax (tag, cls, &type))
    {
      g_free(id);
      return FALSE;
    }
  switch (type)
    {
      case SNMP_INTEGER:
        len = sizeof(glong);
        if (!g_asn1_long_decode (asn1, end, &l))
          {
            g_free(id);
            return FALSE;
          }
        *obj = g_malloc(sizeof(SNMP_OBJECT) + len);
        (*obj)->syntax.l[0] = l;
        break;
      case SNMP_OCTETSTR:
      case SNMP_OPAQUE:
        if (!g_asn1_octets_decode (asn1, end, &p, &len))
          {
            g_free(id);
            return FALSE;
          }
        *obj = g_malloc(sizeof(SNMP_OBJECT) + len);
        memcpy((*obj)->syntax.c, p, len);
        g_free(p);
        break;
      case SNMP_NULL:
      case SNMP_NOSUCHOBJECT:
      case SNMP_NOSUCHINSTANCE:
      case SNMP_ENDOFMIBVIEW:
        len = 0;
        *obj = g_malloc(sizeof(SNMP_OBJECT));
        if (!g_asn1_null_decode (asn1, end))
          {
            g_free(id);
            g_free(*obj);
            *obj = NULL;
            return FALSE;
          }
        break;
      case SNMP_OBJECTID:
        if (!g_asn1_oid_decode (asn1, end, (gulong **)&lp, &len))
          {
            g_free(id);
            return FALSE;
          }
        len *= sizeof(gulong);
        *obj = g_malloc(sizeof(SNMP_OBJECT) + len);
        memcpy((*obj)->syntax.ul, lp, len);
        g_free(lp);
        break;
      case SNMP_IPADDR:
        if (!g_asn1_octets_decode (asn1, end, &p, &len))
          {
            g_free(id);
            return FALSE;
          }
        if ((len != 4) && (len != 16))
          {
            g_free(p);
            g_free(id);
            return FALSE;
          }
        *obj = g_malloc(sizeof(SNMP_OBJECT) + len);
        memcpy((*obj)->syntax.uc, p, len);
        g_free(p);
        break;
      case SNMP_COUNTER:
      case SNMP_GAUGE:
      case SNMP_TIMETICKS:
        len = sizeof(gulong);
        if (!g_asn1_ulong_decode (asn1, end, &ul))
          {
            g_free(id);
            return FALSE;
          }
        *obj = g_malloc(sizeof(SNMP_OBJECT) + len);
        (*obj)->syntax.ul[0] = ul;
        break;
      default:
        g_snmpErrStatus = SNMP_BADVALUE;
        g_free(id);
        return FALSE;
    }
  (*obj)->syntax_len = len;
  (*obj)->type       = type;
  (*obj)->id         = id;
  (*obj)->id_len     = idlen;

  if (!g_asn1_eoc_decode (asn1, eoc))
    {
      g_free(id);
      g_free(*obj);
      *obj = NULL;
      return FALSE;
    }
  return TRUE;
}

/*
 * NAME:        g_snmp_list_encode
 * SYNOPSIS:    gboolean g_snmp_list_encode
 *                  (
 *                      ASN1_SCK     *asn1,
 *                      GSList       *list,
 *                  )
 * DESCRIPTION: Encodes a list of objects in ASN1.
 * RETURNS:     gboolean success.
 */

static gboolean 
g_snmp_list_encode ( ASN1_SCK *asn1, GSList *list)
{
    guchar *eoc;

    if (!g_asn1_eoc_encode (asn1, &eoc))
        return FALSE;

/* Reverse list as we do backwards encoding here */

    list = g_slist_reverse(list);
    while (list)
    {
        if (!g_snmp_object_encode (asn1, list->data))
            return FALSE;
        list = list->next;
    }
    if (!g_asn1_header_encode (asn1, eoc, ASN1_UNI, ASN1_CON, ASN1_SEQ))
        return FALSE;
    return TRUE;
}

/*
 * NAME:        g_snmp_list_decode
 * SYNOPSIS:    gboolean g_snmp_list_decode
 *                  (
 *                      ASN1_SCK     *asn1,
 *                      GSList      **list,
 *                      unsigned     LstSze,
 *                      unsigned     *LstLen
 *                  )
 * DESCRIPTION: Decodes a list of objects from ASN1.
 * RETURNS:     gboolean success.
 */

static gboolean 
g_snmp_list_decode (ASN1_SCK *asn1, GSList **list)
{
    guint cls, con, tag;
    guchar *eoc;
    SNMP_OBJECT *obj;

    *list = NULL;
    if (!g_asn1_header_decode (asn1, &eoc, &cls, &con, &tag))
        return FALSE;
    if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ)
        return FALSE;
    while (!g_asn1_eoc_decode (asn1, eoc))
    {
        if (!g_snmp_object_decode (asn1, &obj))
            return FALSE;
	*list = g_slist_append(*list, obj);
    }
    if (!g_asn1_eoc_decode (asn1, eoc))
        return FALSE;
    return TRUE;
}

/*
 * NAME:        g_snmp_requests_encode
 * SYNOPSIS:    gboolean g_snmp_request_encode
 *                  (
 *                      ASN1_SCK     *asn1,
 *                      SNMP_REQUEST *request
 *                  )
 * DESCRIPTION: Encodes a request or response PDU in ASN1.
 * RETURNS:     gboolean success.
 */

static gboolean 
g_snmp_request_encode ( ASN1_SCK *asn1, SNMP_REQUEST *request)
{
    guchar *end;

    if (!g_asn1_uint_encode (asn1, &end, request->error_index))
        return FALSE;
    if (!g_asn1_header_encode (asn1, end, ASN1_UNI, ASN1_PRI, ASN1_INT))
        return FALSE;
    if (!g_asn1_uint_encode (asn1, &end, request->error_status))
        return FALSE;
    if (!g_asn1_header_encode (asn1, end, ASN1_UNI, ASN1_PRI, ASN1_INT))
        return FALSE;
    if (!g_asn1_ulong_encode (asn1, &end, request->id))
        return FALSE;
    if (!g_asn1_header_encode (asn1, end, ASN1_UNI, ASN1_PRI, ASN1_INT))
        return FALSE;
    return TRUE;
}

/*
 * NAME:        g_snmp_request_decode
 * SYNOPSIS:    gboolean g_snmp_request_decode
 *                  (
 *                      ASN1_SCK     *asn1,
 *                      SNMP_REQUEST *request
 *                  )
 * DESCRIPTION: Decodes a request or response PDU from ASN1.
 * RETURNS:     gboolean success.
 */

static gboolean 
g_snmp_request_decode ( ASN1_SCK *asn1, SNMP_REQUEST *request)
{
    guint cls, con, tag;
    guchar *end;

    if (!g_asn1_header_decode (asn1, &end, &cls, &con, &tag))
        return FALSE;
    if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT)
        return FALSE;
    if (!g_asn1_ulong_decode (asn1, end, &request->id))
        return FALSE;
    if (!g_asn1_header_decode (asn1, &end, &cls, &con, &tag))
        return FALSE;
    if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT)
        return FALSE;
    if (!g_asn1_uint_decode (asn1, end, &request->error_status))
        return FALSE;
    if (!g_asn1_header_decode (asn1, &end, &cls, &con, &tag))
        return FALSE;
    if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT)
        return FALSE;
    if (!g_asn1_uint_decode (asn1, end, &request->error_index))
        return FALSE;
    return TRUE;
}

/*
 * NAME:        g_snmp_trap_encode
 * SYNOPSIS:    gboolean g_snmp_trap_encode
 *                  (
 *                      ASN1_SCK     *asn1,
 *                      SNMP_V1_TRAP *trap
 *                  )
 * DESCRIPTION: Encodes a trap PDU in ASN1.
 * RETURNS:     gboolean success.
 */
static gboolean 
g_snmp_trap_encode ( ASN1_SCK *asn1, SNMP_V1_TRAP *trap)
{
    guchar *end;

    if (!g_asn1_ulong_encode (asn1, &end, trap->time))
        return FALSE;
    if (!g_asn1_header_encode (asn1, end, ASN1_APL, ASN1_PRI, SNMP_TIT))
        return FALSE;
    if (!g_asn1_uint_encode (asn1, &end, trap->specific))
        return FALSE;
    if (!g_asn1_header_encode (asn1, end, ASN1_UNI, ASN1_PRI, ASN1_INT))
        return FALSE;
    if (!g_asn1_uint_encode (asn1, &end, trap->general))
        return FALSE;
    if (!g_asn1_header_encode (asn1, end, ASN1_UNI, ASN1_PRI, ASN1_INT))
        return FALSE;
    if (!g_asn1_octets_encode (asn1, &end, (guchar *)&(trap->ip_address), 4))
        return FALSE;
    if (!g_asn1_header_encode (asn1, end, ASN1_APL, ASN1_PRI, SNMP_IPA))
        return FALSE;
    if (!g_asn1_oid_encode (asn1, &end, trap->id, trap->id_len))
        return FALSE;
    if (!g_asn1_header_encode (asn1, end, ASN1_UNI, ASN1_PRI, ASN1_OJI))
        return FALSE;
    return TRUE;
}

/*
 * NAME:        g_snmp_trap_decode
 * SYNOPSIS:    gboolean g_snmp_trap_decode
 *                  (
 *                      ASN1_SCK     *asn1,
 *                      SNMP_V1_TRAP *trap
 *                  )
 * DESCRIPTION: Decodes a trap PDU from ASN1.
 * RETURNS:     gboolean success.
 */

static gboolean 
g_snmp_trap_decode ( ASN1_SCK *asn1, SNMP_V1_TRAP *trap)
{
    guint cls, con, tag, len;
    guchar *end;

    if (!g_asn1_header_decode (asn1, &end, &cls, &con, &tag))
        return FALSE;
    if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_OJI)
        return FALSE;
    if (!g_asn1_oid_decode (asn1, end, &trap->id, &trap->id_len))
        return FALSE;
    if (!g_asn1_header_decode (asn1, &end, &cls, &con, &tag))
        return FALSE;
    if (!((cls == ASN1_APL && con == ASN1_PRI && tag == SNMP_IPA) ||
		(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS)))	/* needed for Banyan */
        return FALSE;
    if (!g_asn1_octets_decode (asn1, end, (guchar **)&trap->ip_address, &len))
        return FALSE;
    if (!g_asn1_header_decode (asn1, &end, &cls, &con, &tag))
        return FALSE;
    if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT)
        return FALSE;
    if (!g_asn1_uint_decode (asn1, end, &trap->general))
        return FALSE;
    if (!g_asn1_header_decode (asn1, &end, &cls, &con, &tag))
        return FALSE;
    if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT)
        return FALSE;
    if (!g_asn1_uint_decode (asn1, end, &trap->specific))
        return FALSE;
    if (!g_asn1_header_decode (asn1, &end, &cls, &con, &tag))
        return FALSE;
    if (!((cls == ASN1_APL && con == ASN1_PRI && tag == SNMP_TIT) ||
		(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_INT)))
        return FALSE;
    if (!g_asn1_ulong_decode (asn1, end, &trap->time))
        return FALSE;
    return TRUE;
}

/*
 * NAME:        g_snmp_pdu_v1_encode
 * SYNOPSIS:    gboolean g_snmp_pdu_v1_encode
 *                  (
 *                      ASN1_SCK     *asn1,
 *                      SNMP_PDU     *pdu,
 *                  )
 * DESCRIPTION: Encodes a PDU in ASN1.
 *              Pdu is a union of snmp_ror and snmp_trp.
 * RETURNS:     gboolean success.
 */

gboolean 
g_snmp_pdu_v1_encode ( ASN1_SCK *asn1, SNMP_PDU *pdu)
{
    guchar *eoc;

    if (!g_asn1_eoc_encode (asn1, &eoc))
        return FALSE;
    switch (pdu->type)
    {
        case SNMP_PDU_GET:
        case SNMP_PDU_NEXT:
        case SNMP_PDU_RESPONSE:
        case SNMP_PDU_SET:
            if (!g_snmp_list_encode (asn1, pdu->request.variables))
                return FALSE;
            if (!g_snmp_request_encode (asn1, &pdu->request))
                return FALSE;
            break;
        case SNMP_PDU_TRAP1:
            if (!g_snmp_list_encode (asn1, pdu->trap.variables))
                return FALSE;
           if (!g_snmp_trap_encode (asn1, &pdu->trap))
                return FALSE;
            break;
        default:
            return FALSE;
    }
    if (!g_asn1_header_encode (asn1, eoc, ASN1_CTX, ASN1_CON, pdu->type))
        return FALSE;
    return TRUE;
}

/*
 * NAME:        g_snmp_pdu_v2_encode
 * SYNOPSIS:    gboolean g_snmp_pdu_v2_encode
 *                  (
 *                      ASN1_SCK     *asn1,
 *                      SNMP_PDU     *pdu,
 *                  )
 * DESCRIPTION: Encodes a PDU in ASN1.
 *              Pdu is a union of snmp_ror and snmp_trp.
 * RETURNS:     gboolean success.
 */

gboolean 
g_snmp_pdu_v2_encode ( ASN1_SCK *asn1, SNMP_PDU *pdu)
{
    guchar *eoc;

    if (!g_asn1_eoc_encode (asn1, &eoc))
        return FALSE;
    switch (pdu->type)
    {
        case SNMP_PDU_GET:
        case SNMP_PDU_NEXT:
        case SNMP_PDU_RESPONSE:
        case SNMP_PDU_SET:
	case SNMP_PDU_BULK:
	case SNMP_PDU_INFORM:
	case SNMP_PDU_TRAP2:
            if (!g_snmp_list_encode (asn1, pdu->request.variables))
                return FALSE;
            if (!g_snmp_request_encode (asn1, &pdu->request))
                return FALSE;
            break;
        default:
            return FALSE;
    }
    if (!g_asn1_header_encode (asn1, eoc, ASN1_CTX, ASN1_CON, pdu->type))
        return FALSE;
    return TRUE;
}

/*
 * NAME:        g_snmp_pdu_v3_encode
 * SYNOPSIS:    gboolean g_snmp_pdu_v3_encode
 *                  (
 *                      ASN1_SCK     *asn1,
 *                      SNMP_PDU     *pdu,
 *                  )
 * DESCRIPTION: Encodes a PDU in ASN1.
 *              Pdu is a union of snmp_ror and snmp_trp.
 * RETURNS:     gboolean success.
 */

gboolean 
g_snmp_pdu_v3_encode ( ASN1_SCK *asn1, SNMP_PDU *pdu, char *cenid, 
                       int cenidlen, char *cname, int cnamelen)
{
    guchar *eoc, *eoc1, *end;

    if (!g_asn1_eoc_encode (asn1, &eoc1))
        return FALSE;
    if (!g_asn1_eoc_encode (asn1, &eoc))
        return FALSE;
    switch (pdu->type)
    {
        case SNMP_PDU_GET:
        case SNMP_PDU_NEXT:
        case SNMP_PDU_RESPONSE:
        case SNMP_PDU_SET:
	case SNMP_PDU_BULK:
	case SNMP_PDU_INFORM:
	case SNMP_PDU_TRAP2:
            if (!g_snmp_list_encode (asn1, pdu->request.variables))
                return FALSE;
            if (!g_snmp_request_encode (asn1, &pdu->request))
                return FALSE;
            break;
        default:
            return FALSE;
    }

    if (!g_asn1_header_encode (asn1, eoc, ASN1_CTX, ASN1_CON, pdu->type))
        return FALSE;
    if (!g_asn1_octets_encode (asn1, &end, cname, cnamelen))
        return FALSE;
    if (!g_asn1_header_encode (asn1, end, ASN1_UNI, ASN1_PRI, ASN1_OTS))
        return FALSE;
    if (!g_asn1_octets_encode (asn1, &end, cenid, cenidlen))
        return FALSE;
    if (!g_asn1_header_encode (asn1, end, ASN1_UNI, ASN1_PRI, ASN1_OTS))
        return FALSE;
    if (!g_asn1_header_encode (asn1, eoc1, ASN1_UNI, ASN1_CON, ASN1_SEQ))
        return FALSE;
    return TRUE;
}

/*
 * NAME:        g_snmp_pdu_v1_decode
 * SYNOPSIS:    gboolean g_snmp_pdu_v1_decode
 *                  (
 *                      ASN1_SCK     *asn1,
 *                      SNMP_PDU     *pdu
 *                  )
 * DESCRIPTION: Decodes a PDU from ASN1.
 *              pdu is a union of snmp_ror and snmp_trp.
 * RETURNS:     unsigned short success.
 */

gboolean 
g_snmp_pdu_v1_decode ( ASN1_SCK *asn1, SNMP_PDU *pdu)
{
    guint cls, con;
    guchar *eoc;

    if (!g_asn1_header_decode (asn1, &eoc, &cls, &con, &pdu->type))
        return FALSE;
    if (cls != ASN1_CTX || con != ASN1_CON)
        return FALSE;
    switch (pdu->type)
    {
        case SNMP_PDU_GET:
        case SNMP_PDU_NEXT:
        case SNMP_PDU_RESPONSE:
        case SNMP_PDU_SET:
            if (!g_snmp_request_decode (asn1, &pdu->request))
                return FALSE;
            if (!g_snmp_list_decode (asn1, &pdu->request.variables))
                return FALSE;
            break;
        case SNMP_PDU_TRAP1:
            if (!g_snmp_trap_decode (asn1, &pdu->trap))
                return FALSE;
            if (!g_snmp_list_decode (asn1, &pdu->trap.variables))
                return FALSE;
            break;
        default:
            return FALSE;
    }
    if (!g_asn1_eoc_decode (asn1, eoc))
        return FALSE;
    return TRUE;
}

/*
 * NAME:        g_snmp_pdu_v2_decode
 * SYNOPSIS:    gboolean g_snmp_pdu_v2_decode
 *                  (
 *                      ASN1_SCK     *asn1,
 *                      SNMP_PDU     *pdu
 *                  )
 * DESCRIPTION: Decodes a PDU from ASN1.
 *              pdu is snmp_ror.
 * RETURNS:     unsigned short success.
 */

gboolean 
g_snmp_pdu_v2_decode ( ASN1_SCK *asn1, SNMP_PDU *pdu)
{
    guint cls, con;
    guchar *eoc;

    if (!g_asn1_header_decode (asn1, &eoc, &cls, &con, &pdu->type))
        return FALSE;
    if (cls != ASN1_CTX || con != ASN1_CON)
        return FALSE;
    switch (pdu->type)
    {
        case SNMP_PDU_GET:
        case SNMP_PDU_NEXT:
        case SNMP_PDU_RESPONSE:
        case SNMP_PDU_SET:
        case SNMP_PDU_BULK:
        case SNMP_PDU_INFORM:
        case SNMP_PDU_TRAP2:
            if (!g_snmp_request_decode (asn1, &pdu->request))
                return FALSE;
            if (!g_snmp_list_decode (asn1, &pdu->request.variables))
                return FALSE;
            break;
        default:
            return FALSE;
    }
    if (!g_asn1_eoc_decode (asn1, eoc))
        return FALSE;
    return TRUE;
}

/*
 * NAME:        g_snmp_pdu_v3_decode
 * SYNOPSIS:    gboolean g_snmp_pdu_v3_decode
 *                  (
 *                      ASN1_SCK     *asn1,
 *                      SNMP_PDU     *pdu
 *                  )
 * DESCRIPTION: Decodes a PDU from ASN1.
 *              pdu is snmp_ror.
 * RETURNS:     unsigned short success.
 */

gboolean 
g_snmp_pdu_v3_decode ( ASN1_SCK *asn1, SNMP_PDU *pdu, char **cenid, 
                       int *cenidlen, char **cname, int *cnamelen)
{
    guint cls, con;
    guchar *eoc;

    if (!g_asn1_header_decode (asn1, &eoc, &cls, &con, &pdu->type))
        return FALSE;
    if (cls != ASN1_CTX || con != ASN1_CON)
        return FALSE;
    switch (pdu->type)
    {
        case SNMP_PDU_GET:
        case SNMP_PDU_NEXT:
        case SNMP_PDU_RESPONSE:
        case SNMP_PDU_SET:
        case SNMP_PDU_BULK:
        case SNMP_PDU_INFORM:
        case SNMP_PDU_TRAP2:
            if (!g_snmp_request_decode (asn1, &pdu->request))
                return FALSE;
            if (!g_snmp_list_decode (asn1, &pdu->request.variables))
                return FALSE;
            break;
        default:
            return FALSE;
    }
    if (!g_asn1_eoc_decode (asn1, eoc))
        return FALSE;
    return TRUE;
}

/* EOF */
