/* Beholder RMON ethernet network monitor,Copyright (C) 1993 DNPAP group */
/* See file COPYING 'GNU General Public Licence' for copyright details   */
/************************************************************************
** MODULE INFORMATION*
**********************
**     FILE     NAME:       snmp.c
**     SYSTEM   NAME:       SNMP Packet Module
**     ORIGINAL AUTHOR(S):  Dirk Wisse
**     VERSION  NUMBER:     1
**     CREATION DATE:       1990/11/28
**
** DESCRIPTION: SNMP Packet Module
**
*************************************************************************
** CHANGES INFORMATION **
*************************
** REVISION:    $Revision$
** WORKFILE:    $Workfile$
** LOGINFO:     $Log$
*************************************************************************/
#include "dnpap.h"
#include <asn1.h>
#include <snmp.h>
 
/*prototypes*/
STATIC BOOLEAN SnmpSyn2TagCls(unsigned *Tag, unsigned *Cls, int Syn);
STATIC BOOLEAN SnmpTagCls2Syn(unsigned Tag, unsigned Cls, WORD *Syn);
STATIC BOOLEAN SnmpObjEnc(ASN1_SCK *Asn1, SNMP_OBJECT *Obj);
STATIC BOOLEAN SnmpObjDec(ASN1_SCK *Asn1, SNMP_OBJECT *Obj);
STATIC BOOLEAN SnmpLstEnc(ASN1_SCK *Asn1, SNMP_OBJECT *Lst, unsigned LstLen);
STATIC BOOLEAN SnmpLstDec(ASN1_SCK *Asn1, SNMP_OBJECT *Lst, unsigned LstSze, unsigned *LstLen);
STATIC BOOLEAN SnmpRqsEnc(ASN1_SCK *Asn1, SNMP_REQUEST *Rqs);
STATIC BOOLEAN SnmpRqsDec(ASN1_SCK *Asn1, SNMP_REQUEST *Rqs);
STATIC BOOLEAN SnmpTrpEnc(ASN1_SCK *Asn1, SNMP_TRAP *Trp);
STATIC BOOLEAN SnmpTrpDec(ASN1_SCK *Asn1, SNMP_TRAP *Trp);
STATIC BOOLEAN SnmpPduEnc(ASN1_SCK *Asn1, SNMP_PDU *Pdu, SNMP_OBJECT *Lst, unsigned LstLen);
STATIC BOOLEAN SnmpPduDec(ASN1_SCK *Asn1, SNMP_PDU *Pdu, SNMP_OBJECT *Lst, unsigned LstSze, unsigned *LstLen);

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


INT snmpErrStatus = SNMP_NOERROR;


typedef struct _SNMP_CNV SNMP_CNV;


struct _SNMP_CNV
{
    unsigned Class;
    unsigned Tag;
    int      Syntax;
};



SNMP_STAT SnmpStat;

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},
    {ASN1_APL, SNMP_GGE, SNMP_GAUGE},
    {ASN1_APL, SNMP_TIT, SNMP_TIMETICKS},
    {ASN1_APL, SNMP_OPQ, SNMP_OPAQUE},
    {0,       0,       -1}
};

/**************************************************************
** NAME:        SnmpSyn2TagCls
** SYNOPSIS:    BOOLEAN SnmpSyn2TagCls
**                  (
**                      unsigned    *Tag,
**                      unsigned    *Cls,
**                      int         Syn
**                  )
** DESCRIPTION: Converts Syntax tag to ASN1 tag and class.
**              See SnmpCnv for conversion.
** RETURNS:     BOOLEAN success.
**************************************************************/
BOOLEAN SnmpSyn2TagCls
    (
        unsigned    *Tag,
        unsigned    *Cls,
        int         Syn
    )
{
    SNMP_CNV
        *Cnv;

    Cnv = SnmpCnv;
    while (Cnv->Syntax != -1)
    {
        if (Cnv->Syntax == Syn)
        {
            *Tag = Cnv->Tag;
            *Cls = Cnv->Class;
            return TRUE;
        }
        Cnv++;
    }
    return FALSE;
}

/**************************************************************
** NAME:        SnmpTagCls2Syn
** SYNOPSIS:    BOOLEAN SnmpTagCls2Syn
**                  (
**                      unsigned    Tag,
**                      unsigned    Cls,
**                      WORD        *Syn
**                  )
** DESCRIPTION: Converts ASN1 tag and class to Syntax tag.
**              See SnmpCnv for conversion.
** RETURNS:     BOOLEAN success.
**************************************************************/
BOOLEAN SnmpTagCls2Syn
    (
        unsigned    Tag,
        unsigned    Cls,
        WORD        *Syn
    )
{
    SNMP_CNV
        *Cnv;

    Cnv = SnmpCnv;
    while (Cnv->Syntax != -1)
    {
        if (Cnv->Tag == Tag && Cnv->Class == Cls)
        {
            *Syn = Cnv->Syntax;
            return TRUE;
        }
        Cnv++;
    }
    return FALSE;
}

/**************************************************************
** NAME:        SnmpObjEnc
** SYNOPSIS:    BOOLEAN SnmpObjEnc
**                  (
**                      ASN1_SCK     *Asn1,
**                      SNMP_OBJECT   *Obj,
**                  )
** DESCRIPTION: Encodes an object in ASN1.
** RETURNS:     BOOLEAN success.
**************************************************************/
BOOLEAN SnmpObjEnc
    (
        ASN1_SCK     *Asn1,
        SNMP_OBJECT   *Obj
    )
{
    unsigned
        Cls,
        Tag;
    BYTE
        *Eoc,
        *End;

    if (!Asn1EocEnc (Asn1, &Eoc))
        return FALSE;
    switch (Obj->Type)
    {
        case SNMP_INTEGER:
            if (!Asn1IntEncLng (Asn1, &End, Obj->Syntax.LngInt))
                return FALSE;
            break;
        case SNMP_OCTETSTR:
        case SNMP_OPAQUE:
            if (!Asn1OtsEnc (Asn1, &End, Obj->Syntax.BufChr, Obj->SyntaxLen))
                return FALSE;
            break;
        case SNMP_NULL:
            if (!Asn1NulEnc (Asn1, &End))
                return FALSE;
            break;
        case SNMP_OBJECTID:
            if (!Asn1OjiEnc (Asn1, &End, Obj->Syntax.BufInt, Obj->SyntaxLen))
                return FALSE;
            break;
        case SNMP_IPADDR:
            if (!Asn1OtsEnc (Asn1, &End, (BYTE *)&Obj->Syntax.LngUns, 4))
                return FALSE;
            break;
        case SNMP_COUNTER:
        case SNMP_GAUGE:
        case SNMP_TIMETICKS:
            if (!Asn1IntEncLngUns (Asn1, &End, Obj->Syntax.LngUns))
                return FALSE;
            break;
        default:
            return FALSE;
    }
    if (!SnmpSyn2TagCls (&Tag, &Cls, Obj->Type))
        return FALSE;
    if (!Asn1HdrEnc (Asn1, End, Cls, ASN1_PRI, Tag))
        return FALSE;
    if (!Asn1OjiEnc (Asn1, &End, Obj->Id, Obj->IdLen))
        return FALSE;
    if (!Asn1HdrEnc (Asn1, End, ASN1_UNI, ASN1_PRI, ASN1_OJI))
        return FALSE;
    if (!Asn1HdrEnc (Asn1, Eoc, ASN1_UNI, ASN1_CON, ASN1_SEQ))
        return FALSE;
    return TRUE;
}

/**************************************************************
** NAME:        SnmpObjDec
** SYNOPSIS:    BOOLEAN SnmpObjDec
**                  (
**                      ASN1_SCK     *Asn1,
**                      SNMP_OBJECT   *Obj,
**                  )
** DESCRIPTION: Decodes an object from ASN1.
** RETURNS:     BOOLEAN success.
**************************************************************/
BOOLEAN SnmpObjDec
    (
        ASN1_SCK     *Asn1,
        SNMP_OBJECT   *Obj
    )
{
    unsigned
        Cls,
        Con,
        Tag;
    BYTE
        *Eoc,
        *End;

    if (!Asn1HdrDec (Asn1, &Eoc, &Cls, &Con, &Tag))
        return FALSE;
    if (Cls != ASN1_UNI || Con != ASN1_CON || Tag != ASN1_SEQ)
        return FALSE;
    if (!Asn1HdrDec (Asn1, &End, &Cls, &Con, &Tag))
        return FALSE;
    if (Cls != ASN1_UNI || Con != ASN1_PRI || Tag != ASN1_OJI)
        return FALSE;
    if (!Asn1OjiDec (Asn1, End, Obj->Id, SNMP_SIZE_OBJECTID, &Obj->IdLen))
        return FALSE;
    if (!Asn1HdrDec (Asn1, &End, &Cls, &Con, &Tag))
        return FALSE;
    if (Con != ASN1_PRI)
        return FALSE;
    if (!SnmpTagCls2Syn (Tag, Cls, &Obj->Type))
        return FALSE;
    switch (Obj->Type)
    {
        case SNMP_INTEGER:
            if (!Asn1IntDecLng (Asn1, End, &Obj->Syntax.LngInt))
                return FALSE;
            break;
        case SNMP_OCTETSTR:
        case SNMP_OPAQUE:
            if (!Asn1OtsDec (Asn1, End, Obj->Syntax.BufChr, SNMP_SIZE_BUFCHR,
                &Obj->SyntaxLen))
                return FALSE;
            break;
        case SNMP_NULL:
            if (!Asn1NulDec (Asn1, End))
                return FALSE;
            break;
        case SNMP_OBJECTID:
            if (!Asn1OjiDec (Asn1, End, Obj->Syntax.BufInt, SNMP_SIZE_BUFINT,
                &Obj->SyntaxLen))
                return FALSE;
            break;
        case SNMP_IPADDR:
            if (!Asn1OtsDec (Asn1, End, (BYTE *)&Obj->Syntax.LngUns, 4,
                &Obj->SyntaxLen))
                return FALSE;
            if (Obj->SyntaxLen != 4)
                return FALSE;
            break;
        case SNMP_COUNTER:
        case SNMP_GAUGE:
        case SNMP_TIMETICKS:
            if (!Asn1IntDecLngUns (Asn1, End, &Obj->Syntax.LngUns))
                return FALSE;
            break;
        default:
            return FALSE;
    }
    if (!Asn1EocDec (Asn1, Eoc))
        return FALSE;
    return TRUE;
}

/**************************************************************
** NAME:        SnmpLstEnc
** SYNOPSIS:    BOOLEAN
**                  SnmpLstEnc
**                  (
**                      ASN1_SCK     *Asn1,
**                      SNMP_OBJECT   *Lst,
**                      unsigned     LstLen
**                  )
** DESCRIPTION: Encodes a list of objects in ASN1.
** RETURNS:     BOOLEAN success.
**************************************************************/
BOOLEAN
    SnmpLstEnc
    (
        ASN1_SCK     *Asn1,
        SNMP_OBJECT  *Lst,
        unsigned     LstLen
    )
{
    BYTE
        *Eoc;

    if (!Asn1EocEnc (Asn1, &Eoc))
        return FALSE;
    Lst += LstLen;
    while (LstLen-- > 0)
    {
        if (!SnmpObjEnc (Asn1, --Lst))
            return FALSE;
    }
    if (!Asn1HdrEnc (Asn1, Eoc, ASN1_UNI, ASN1_CON, ASN1_SEQ))
        return FALSE;
    return TRUE;
}

/**************************************************************
** NAME:        SnmpLstDec
** SYNOPSIS:    BOOLEAN
**                  SnmpLstDec
**                  (
**                      ASN1_SCK     *Asn1,
**                      SNMP_OBJECT   *Lst,
**                      unsigned     LstSze,
**                      unsigned     *LstLen
**                  )
** DESCRIPTION: Decodes a list of objects from ASN1.
** RETURNS:     BOOLEAN success.
**************************************************************/
BOOLEAN
    SnmpLstDec
    (
        ASN1_SCK     *Asn1,
        SNMP_OBJECT   *Lst,
        unsigned     LstSze,
        unsigned     *LstLen
    )
{
    unsigned
        Cls,
        Con,
        Tag;
    BYTE
        *Eoc;

    if (!Asn1HdrDec (Asn1, &Eoc, &Cls, &Con, &Tag))
        return FALSE;
    if (Cls != ASN1_UNI || Con != ASN1_CON || Tag != ASN1_SEQ)
        return FALSE;
    *LstLen = 0;
    while (!Asn1Eoc (Asn1, Eoc))
    {
        if (++(*LstLen) > LstSze)
        {
            snmpErrStatus = SNMP_TOOBIG;
            return FALSE;
        }
        if (!SnmpObjDec (Asn1, Lst++))
            return FALSE;
    }
    if (!Asn1EocDec (Asn1, Eoc))
        return FALSE;
    return TRUE;
}

/**************************************************************
** NAME:        SnmpRqsEnc
** SYNOPSIS:    BOOLEAN
**                  SnmpRqsEnc
**                  (
**                      ASN1_SCK     *Asn1,
**                      SNMP_REQUEST *Rqs
**                  )
** DESCRIPTION: Encodes a request or response PDU in ASN1.
** RETURNS:     BOOLEAN success.
**************************************************************/
BOOLEAN
    SnmpRqsEnc
    (
        ASN1_SCK     *Asn1,
        SNMP_REQUEST *Rqs
    )
{
    BYTE
        *End;

    if (!Asn1IntEncUns (Asn1, &End, Rqs->ErrorIndex))
        return FALSE;
    if (!Asn1HdrEnc (Asn1, End, ASN1_UNI, ASN1_PRI, ASN1_INT))
        return FALSE;
    if (!Asn1IntEncUns (Asn1, &End, Rqs->ErrorStatus))
        return FALSE;
    if (!Asn1HdrEnc (Asn1, End, ASN1_UNI, ASN1_PRI, ASN1_INT))
        return FALSE;
    if (!Asn1IntEncLngUns (Asn1, &End, Rqs->Id))
        return FALSE;
    if (!Asn1HdrEnc (Asn1, End, ASN1_UNI, ASN1_PRI, ASN1_INT))
        return FALSE;
    return TRUE;
}

/**************************************************************
** NAME:        SnmpRqsDec
** SYNOPSIS:    BOOLEAN
**                  SnmpRqsDec
**                  (
**                      ASN1_SCK     *Asn1,
**                      SNMP_REQUEST *Rqs
**                  )
** DESCRIPTION: Decodes a request or response PDU from ASN1.
** RETURNS:     BOOLEAN success.
**************************************************************/
BOOLEAN
    SnmpRqsDec
    (
        ASN1_SCK     *Asn1,
        SNMP_REQUEST *Rqs
    )
{
    unsigned
        Cls,
        Con,
        Tag;
    BYTE
        *End;

    if (!Asn1HdrDec (Asn1, &End, &Cls, &Con, &Tag))
        return FALSE;
    if (Cls != ASN1_UNI || Con != ASN1_PRI || Tag != ASN1_INT)
        return FALSE;
    if (!Asn1IntDecLngUns (Asn1, End, &Rqs->Id))
        return FALSE;
    if (!Asn1HdrDec (Asn1, &End, &Cls, &Con, &Tag))
        return FALSE;
    if (Cls != ASN1_UNI || Con != ASN1_PRI || Tag != ASN1_INT)
        return FALSE;
    if (!Asn1IntDecUns (Asn1, End, &Rqs->ErrorStatus))
        return FALSE;
    if (!Asn1HdrDec (Asn1, &End, &Cls, &Con, &Tag))
        return FALSE;
    if (Cls != ASN1_UNI || Con != ASN1_PRI || Tag != ASN1_INT)
        return FALSE;
    if (!Asn1IntDecUns (Asn1, End, &Rqs->ErrorIndex))
        return FALSE;
    return TRUE;
}

/**************************************************************
** NAME:        SnmpTrpEnc
** SYNOPSIS:    BOOLEAN
**                  SnmpTrpEnc
**                  (
**                      ASN1_SCK     *Asn1,
**                      SNMP_TRAP    *Trp
**                  )
** DESCRIPTION: Encodes a trap PDU in ASN1.
** RETURNS:     BOOLEAN success.
**************************************************************/
BOOLEAN
    SnmpTrpEnc
    (
        ASN1_SCK     *Asn1,
        SNMP_TRAP    *Trp
    )
{
    BYTE
        *End;

    if (!Asn1IntEncLngUns (Asn1, &End, Trp->Time))
        return FALSE;
    if (!Asn1HdrEnc (Asn1, End, ASN1_APL, ASN1_PRI, SNMP_TIT))
        return FALSE;
    if (!Asn1IntEncUns (Asn1, &End, Trp->Specific))
        return FALSE;
    if (!Asn1HdrEnc (Asn1, End, ASN1_UNI, ASN1_PRI, ASN1_INT))
        return FALSE;
    if (!Asn1IntEncUns (Asn1, &End, Trp->General))
        return FALSE;
    if (!Asn1HdrEnc (Asn1, End, ASN1_UNI, ASN1_PRI, ASN1_INT))
        return FALSE;
    if (!Asn1OtsEnc (Asn1, &End, (BYTE *)&(Trp->IpAddress), 4))
        return FALSE;
    if (!Asn1HdrEnc (Asn1, End, ASN1_APL, ASN1_PRI, SNMP_IPA))
        return FALSE;
    if (!Asn1OjiEnc (Asn1, &End, Trp->Id, Trp->IdLen))
        return FALSE;
    if (!Asn1HdrEnc (Asn1, End, ASN1_UNI, ASN1_PRI, ASN1_OJI))
        return FALSE;
    return TRUE;
}

/**************************************************************
** NAME:        SnmpTrpDec
** SYNOPSIS:    BOOLEAN
**                  SnmpTrpDec
**                  (
**                      ASN1_SCK     *Asn1,
**                      SNMP_TRAP    *Trp
**                  )
** DESCRIPTION: Decodes a trap PDU from ASN1.
** RETURNS:     BOOLEAN success.
**************************************************************/
BOOLEAN
    SnmpTrpDec
    (
        ASN1_SCK     *Asn1,
        SNMP_TRAP    *Trp
    )
{
    unsigned
        Cls,
        Con,
        Tag;
    BYTE
        *End;

    if (!Asn1HdrDec (Asn1, &End, &Cls, &Con, &Tag))
        return FALSE;
    if (Cls != ASN1_UNI || Con != ASN1_PRI || Tag != ASN1_OJI)
        return FALSE;
    if (!Asn1OjiDec (Asn1, End, Trp->Id, sizeof(Trp->Id)/sizeof(Trp->Id[0]), &Trp->IdLen))
        return FALSE;
    if (!Asn1HdrDec (Asn1, &End, &Cls, &Con, &Tag))
        return FALSE;
    if (Cls != ASN1_APL || Con != ASN1_PRI || Tag != SNMP_IPA)
        return FALSE;
    if (!Asn1OtsDec (Asn1, End, (BYTE *)&(Trp->IpAddress), 4, &Tag))
        return FALSE;
    if (!Asn1HdrDec (Asn1, &End, &Cls, &Con, &Tag))
        return FALSE;
    if (Cls != ASN1_UNI || Con != ASN1_PRI || Tag != ASN1_INT)
        return FALSE;
    if (!Asn1IntDecUns (Asn1, End, &Trp->General))
        return FALSE;
    if (!Asn1HdrDec (Asn1, &End, &Cls, &Con, &Tag))
        return FALSE;
    if (Cls != ASN1_UNI || Con != ASN1_PRI || Tag != ASN1_INT)
        return FALSE;
    if (!Asn1IntDecUns (Asn1, End, &Trp->Specific))
        return FALSE;
    if (!Asn1HdrDec (Asn1, &End, &Cls, &Con, &Tag))
        return FALSE;
    if (Cls != ASN1_APL || Con != ASN1_PRI || Tag != SNMP_TIT)
        return FALSE;
    if (!Asn1IntDecLngUns (Asn1, End, &Trp->Time))
        return FALSE;
    return TRUE;
}

/**************************************************************
** NAME:        SnmpPduEnc
** SYNOPSIS:    BOOLEAN
**                  SnmpPduEnc
**                  (
**                      ASN1_SCK     *Asn1,
**                      SNMP_PDU     *Pdu,
**                      SNMP_OBJECT   *Lst,
**                      unsigned     LstLen
**                  )
** DESCRIPTION: Encodes a PDU in ASN1.
**              Pdu is a union of snmp_ror and snmp_trp.
** RETURNS:     BOOLEAN success.
**************************************************************/
BOOLEAN
    SnmpPduEnc
    (
        ASN1_SCK     *Asn1,
        SNMP_PDU     *Pdu,
        SNMP_OBJECT   *Lst,
        unsigned     LstLen
    )
{
    BYTE
        *Eoc;

    if (!Asn1EocEnc (Asn1, &Eoc))
        return FALSE;
    if (!SnmpLstEnc (Asn1, Lst, LstLen))
        return FALSE;
    switch (Pdu->Type)
    {
        case SNMP_PDU_GET:
        case SNMP_PDU_NEXT:
        case SNMP_PDU_RESPONSE:
        case SNMP_PDU_SET:
            if (!SnmpRqsEnc (Asn1, &Pdu->Request))
                return FALSE;
            break;
        case SNMP_PDU_TRAP:
           if (!SnmpTrpEnc (Asn1, &Pdu->Trap))
                return FALSE;
            break;
        default:
            return FALSE;
    }
    if (!Asn1HdrEnc (Asn1, Eoc, ASN1_CTX, ASN1_CON, Pdu->Type))
        return FALSE;
    return TRUE;
}

/**************************************************************
** NAME:        SnmpPduDec
** SYNOPSIS:    BOOLEAN
**                  SnmpPduDec
**                  (
**                      ASN1_SCK     *Asn1,
**                      SNMP_PDU     *Pdu
**                      SNMP_OBJECT   *Lst,
**                      unsigned     LstSze,
**                      unsigned     *LstLen
**                  )
** DESCRIPTION: Decodes a PDU from ASN1.
**              Pdu is a union of snmp_ror and snmp_trp.
** RETURNS:     BOOLEAN success.
**************************************************************/
BOOLEAN
    SnmpPduDec
    (
        ASN1_SCK     *Asn1,
        SNMP_PDU     *Pdu,
        SNMP_OBJECT   *Lst,
        unsigned     LstSze,
        unsigned     *LstLen
    )
{
    unsigned
        Cls,
        Con;
    BYTE
        *Eoc;

    if (!Asn1HdrDec (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 (!SnmpRqsDec (Asn1, &Pdu->Request))
                return FALSE;
            break;
        case SNMP_PDU_TRAP:
            if (!SnmpTrpDec (Asn1, &Pdu->Trap))
                return FALSE;
            break;
        default:
            SnmpStat.InBadTypes++;
            return FALSE;
    }
    if (!SnmpLstDec (Asn1, Lst, LstSze, LstLen))
        return FALSE;
    if (!Asn1EocDec (Asn1, Eoc))
        return FALSE;
    return TRUE;
}

/**************************************************************
** NAME:        SnmpEnc                                   [API]
** SYNOPSIS:    BOOLEAN SnmpEnc
**                  (
**                      BYTE        **Snmp, 
**                      unsigned    *SnmpLen,
**                      SNMP_PDU    *Pdu, 
**                      BYTE        *Com, 
**                      unsigned    ComLen, 
**                      SNMP_OBJECT  *Lst, 
**                      unsigned    LstLen
**                  )
** DESCRIPTION: Encodes a request, response or trap in ASN1.
** RETURNS:     BOOLEAN success.
**************************************************************/
BOOLEAN SnmpEnc(BYTE        **Snmp, 
                unsigned    *SnmpLen,
                SNMP_PDU    *Pdu, 
                BYTE        *Com, 
                unsigned    ComLen, 
                SNMP_OBJECT  *Lst, 
                unsigned    LstLen)
{
    BYTE
        *Eoc,
        *End;
    ASN1_SCK
        Asn1;

    snmpErrStatus = SNMP_GENERROR;

    Asn1Opn (&Asn1, *Snmp, *SnmpLen, ASN1_ENC);
    if (!Asn1EocEnc (&Asn1, &Eoc))
        return FALSE;
    if (!SnmpPduEnc (&Asn1, Pdu, Lst, LstLen))
        return FALSE;
    if (!Asn1OtsEnc (&Asn1, &End, Com, ComLen))
        return FALSE;
    if (!Asn1HdrEnc (&Asn1, End, ASN1_UNI, ASN1_PRI, ASN1_OTS))
        return FALSE;
    if (!Asn1IntEncUns (&Asn1, &End, SNMP_VERSION))
        return FALSE;
    if (!Asn1HdrEnc (&Asn1, End, ASN1_UNI, ASN1_PRI, ASN1_INT))
        return FALSE;
    if (!Asn1HdrEnc (&Asn1, Eoc, ASN1_UNI, ASN1_CON, ASN1_SEQ))
        return FALSE;
    Asn1Cls (&Asn1, Snmp, SnmpLen);
    
    SnmpStat.OutPkts++;
    switch (Pdu->Type)
    {
        case SNMP_PDU_GET:
            SnmpStat.OutGetRequests++;
            break;
        case SNMP_PDU_NEXT:
            SnmpStat.OutGetNexts++;
            break;
        case SNMP_PDU_RESPONSE:
            SnmpStat.OutGetResponses++;
            break;
        case SNMP_PDU_SET:
            SnmpStat.OutSetRequests++;
            break;
        case SNMP_PDU_TRAP:
            SnmpStat.OutTraps++;
            break;
    }
    
    return TRUE;
}

/**************************************************************
** NAME:        SnmpDec                                   [API]
** SYNOPSIS:    BOOLEAN SnmpDec
**                  (
**                      BYTE        *Snmp, 
**                      unsigned    SnmpLen,
**                      SNMP_PDU    *Pdu,
**                      BYTE        *Com, 
**                      unsigned    ComSze,
**                      unsigned    *ComLen, 
**                      SNMP_OBJECT  *Lst, 
**                      unsigned    LstSze,
**                      unsigned    *LstLen
**                  )
** DESCRIPTION: Decodes a request, response or trap from ASN1.
** RETURNS:     BOOLEAN success.
**************************************************************/
BOOLEAN SnmpDec(BYTE        *Snmp, 
                unsigned    SnmpLen,
                SNMP_PDU    *Pdu,
                BYTE        *Com, 
                unsigned    ComSze,
                unsigned    *ComLen, 
                SNMP_OBJECT  *Lst, 
                unsigned    LstSze,
                unsigned    *LstLen)
{
    unsigned
        Cls,
        Con,
        Tag;
    BYTE
        *Eoc,
        *End;
    ASN1_SCK
        Asn1;
    unsigned
        Ver;

    snmpErrStatus = SNMP_GENERROR;

    Asn1Opn (&Asn1, Snmp, SnmpLen, ASN1_DEC);
    if (!Asn1HdrDec (&Asn1, &Eoc, &Cls, &Con, &Tag))
    {
        SnmpStat.InASNParseErrs++;
        return FALSE;
    }
    if (Cls != ASN1_UNI || Con != ASN1_CON || Tag != ASN1_SEQ)
    {
        SnmpStat.InASNParseErrs++;
        return FALSE;
    }
    if (!Asn1HdrDec (&Asn1, &End, &Cls, &Con, &Tag))
    {
        SnmpStat.InASNParseErrs++;
        return FALSE;
    }
    if (Cls != ASN1_UNI || Con != ASN1_PRI || Tag != ASN1_INT)
    {
        SnmpStat.InASNParseErrs++;
        return FALSE;
    }
    if (!Asn1IntDecUns (&Asn1, End, &Ver))
    {
        SnmpStat.InASNParseErrs++;
        return FALSE;
    }
    if (!Asn1HdrDec (&Asn1, &End, &Cls, &Con, &Tag))
    {
        SnmpStat.InASNParseErrs++;
        return FALSE;
    }
    if (Cls != ASN1_UNI || Con != ASN1_PRI || Tag != ASN1_OTS)
    {
        SnmpStat.InASNParseErrs++;
        return FALSE;
    }
    if (!Asn1OtsDec (&Asn1, End, Com, ComSze, ComLen))
    {
        SnmpStat.InASNParseErrs++;
        return FALSE;
    }
    if (Ver != SNMP_VERSION)
    {
        SnmpStat.InBadVersions++;
        return FALSE;
    }
    if (!SnmpPduDec (&Asn1, Pdu, Lst, LstSze, LstLen))
    {
        SnmpStat.InASNParseErrs++;
        return FALSE;
    }
    if (!Asn1EocDec (&Asn1, Eoc))
    {
        SnmpStat.InASNParseErrs++;
        return FALSE;
    }
    Asn1Cls (&Asn1, &Snmp, &SnmpLen);
    
    SnmpStat.InPkts++;
    switch (Pdu->Type)
    {
        case SNMP_PDU_GET:
            SnmpStat.InGetRequests++;
            break;
        case SNMP_PDU_NEXT:
            SnmpStat.InGetNexts++;
            break;
        case SNMP_PDU_RESPONSE:
            SnmpStat.InGetResponses++;
            break;
        case SNMP_PDU_SET:
            SnmpStat.InSetRequests++;
            break;
        case SNMP_PDU_TRAP:
            SnmpStat.InTraps++;
            break;
    }
    
    return TRUE;
}
