/************************************************************************
** MODULE INFORMATION*
**********************
**     FILE     NAME:       pkt.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$
*************************************************************************/
#if ! defined(PRD)
static char _pvcs_hdr[] =
"$Header$";
#endif
#include "asn1.h"
#include "snmp.h"
#include "aut.h"
#include "pkt.h"

pkt_sck
    PktSck;

static pkt_cnv
    PktCnv [] =
{
    {ASN_UNI, ASN_NUL, PKT_NULL},
    {ASN_UNI, ASN_INT, PKT_INTEGER},
    {ASN_UNI, ASN_OTS, PKT_OCTETSTRING},
    {ASN_UNI, ASN_OJI, PKT_OBJECTIDENTIFIER},
    {ASN_APL, PKT_IPA, PKT_IPADDRESS},
    {ASN_APL, PKT_CNT, PKT_COUNTER},
    {ASN_APL, PKT_GGE, PKT_GAUGE},
    {ASN_APL, PKT_TIT, PKT_TIMETICKS},
    {ASN_APL, PKT_OPQ, PKT_OPAQUE},
    {0,       0,       -1}
};

/**************************************************************
** NAME:        PktSyn2TagCls
** SYNOPSIS:    int
**                  PktSyn2TagCls
**                  (
**                      unsigned    *Tag,
**                      unsigned    *Cls,
**                      int         Syn
**                  )
** DESCRIPTION: Converts Syntax tag to ASN1 tag and class.
**              See PktCnv for conversion.
** RETURNS:     0 -->   no error
**              else    error code
**************************************************************/
int PktSyn2TagCls (Tag,Cls,Syn)
unsigned    *Tag;
unsigned    *Cls;
int         Syn;
{
    pkt_cnv
        *Cnv;

    Cnv = PktCnv;
    while (Cnv->CnvSyn != -1)
    {
        if (Cnv->CnvSyn == Syn)
        {
            *Tag = Cnv->CnvTag;
            *Cls = Cnv->CnvCls;
            return (0);
        }
        Cnv++;
    }
    return (-1);
}

/**************************************************************
** NAME:        PktTagCls2Syn
** SYNOPSIS:    int
**                  PktTagCls2Syn
**                  (
**                      unsigned    Tag,
**                      unsigned    Cls,
**                      int         *Syn
**                  )
** DESCRIPTION: Converts ASN1 tag and class to Syntax tag.
**              See PktCnv for conversion.
** RETURNS:     0 -->   no error
**              else    error code
**************************************************************/
int PktTagCls2Syn (Tag,Cls,Syn)
unsigned    Tag;
unsigned    Cls;
int         *Syn;
{
    pkt_cnv
        *Cnv;

    Cnv = PktCnv;
    while (Cnv->CnvSyn != -1)
    {
        if (Cnv->CnvTag == Tag && Cnv->CnvCls == Cls)
        {
            *Syn = Cnv->CnvSyn;
            return (0);
        }
        Cnv++;
    }
    return (-1);
}

/**************************************************************
** NAME:        PktObjEnc
** SYNOPSIS:    int
**                  PktObjEnc
**                  (
**                      asn_sck     *Asn,
**                      pkt_obj     *Obj,
**                  )
** DESCRIPTION: Encodes an object in ASN1.
** RETURNS:     0 -->   no error
**              else    error code
**************************************************************/
int PktObjEnc (Asn,Obj)
asn_sck     *Asn;
pkt_obj     *Obj;
{
    unsigned
        Cls,
        Tag;
    char
        *Eoc,
        *End;

    if (AsnEocEnc (Asn, &Eoc) < 0)
        return (-1);
    switch (Obj->ObjTag)
    {
        case PKT_INTEGER:
            if (AsnIntEncLng (Asn, &End, Obj->ObjSyn.SynLngInt) < 0)
                return (-1);
            break;
        case PKT_OCTETSTRING:
        case PKT_OPAQUE:
            if (AsnOtsEnc (Asn, &End, Obj->ObjSyn.SynBufChr, Obj->ObjSynLen) < 0)
                return (-1);
            break;
        case PKT_NULL:
            if (AsnNulEnc (Asn, &End) < 0)
                return (-1);
            break;
        case PKT_OBJECTIDENTIFIER:
            if (AsnOjiEnc (Asn, &End, Obj->ObjSyn.SynBufInt, Obj->ObjSynLen) < 0)
                return (-1);
            break;
        case PKT_IPADDRESS:
            if (AsnOtsEnc (Asn, &End, (char *)&Obj->ObjSyn.SynLngUns, 4) < 0)
                return (-1);
            break;
        case PKT_COUNTER:
        case PKT_GAUGE:
        case PKT_TIMETICKS:
            if (AsnIntEncLngUns (Asn, &End, Obj->ObjSyn.SynLngUns) < 0)
                return (-1);
            break;
        default:
            return (-1);
    }
    if (PktSyn2TagCls (&Tag, &Cls, Obj->ObjTag) < 0)
        return (-1);
    if (AsnHdrEnc (Asn, End, Cls, ASN_PRI, Tag) < 0)
        return (-1);
    if (AsnOjiEnc (Asn, &End, Obj->ObjOji, Obj->ObjOjiLen) < 0)
        return (-1);
    if (AsnHdrEnc (Asn, End, ASN_UNI, ASN_PRI, ASN_OJI) < 0)
        return (-1);
    if (AsnHdrEnc (Asn, Eoc, ASN_UNI, ASN_CON, ASN_SEQ) < 0)
        return (-1);
    return (0);
}

/**************************************************************
** NAME:        PktObjDec
** SYNOPSIS:    int
**                  PktObjDec
**                  (
**                      asn_sck     *Asn,
**                      pkt_obj     *Obj,
**                  )
** DESCRIPTION: Decodes an object from ASN1.
** RETURNS:     0 -->   no error
**              else    error code
**************************************************************/
int PktObjDec (Asn,Obj)
asn_sck     *Asn;
pkt_obj     *Obj;
{
    unsigned
        Cls,
        Con,
        Tag;
    char
        *Eoc,
        *End;

    if (AsnHdrDec (Asn, &Eoc, &Cls, &Con, &Tag) < 0)
        return (-1);
    if (Cls != ASN_UNI || Con != ASN_CON || Tag != ASN_SEQ)
        return (-1);
    if (AsnHdrDec (Asn, &End, &Cls, &Con, &Tag) < 0)
        return (-1);
    if (Cls != ASN_UNI || Con != ASN_PRI || Tag != ASN_OJI)
        return (-1);
    if (AsnOjiDec (Asn, End, Obj->ObjOji, PKT_SZEOJI, &Obj->ObjOjiLen) < 0)
        return (-1);
    if (AsnHdrDec (Asn, &End, &Cls, &Con, &Tag) < 0)
        return (-1);
    if (Con != ASN_PRI)
        return (-1);
    if (PktTagCls2Syn (Tag, Cls, &Obj->ObjTag) < 0)
        return (-1);
    switch (Obj->ObjTag)
    {
        case PKT_INTEGER:
            if (AsnIntDecLng (Asn, End, &Obj->ObjSyn.SynLngInt) < 0)
                return (-1);
            break;
        case PKT_OCTETSTRING:
        case PKT_OPAQUE:
            if (AsnOtsDec (Asn, End, Obj->ObjSyn.SynBufChr, PKT_SZESYNCHR,
                &Obj->ObjSynLen) < 0)
                return (-1);
            break;
        case PKT_NULL:
            if (AsnNulDec (Asn, End) < 0)
                return (-1);
            break;
        case PKT_OBJECTIDENTIFIER:
            if (AsnOjiDec (Asn, End, Obj->ObjSyn.SynBufInt, PKT_SZESYNINT,
                &Obj->ObjSynLen) < 0)
                return (-1);
            break;
        case PKT_IPADDRESS:
            if (AsnOtsDec (Asn, End, (char *)&Obj->ObjSyn.SynLngUns, 4, &Tag) < 0)
                return (-1);
            break;
        case PKT_COUNTER:
        case PKT_GAUGE:
        case PKT_TIMETICKS:
            if (AsnIntDecLngUns (Asn, End, &Obj->ObjSyn.SynLngUns) < 0)
                return (-1);
            break;
        default:
            return (-1);
    }
    if (AsnEocDec (Asn, Eoc) < 0)
        return (-1);
    return (0);
}

/**************************************************************
** NAME:        PktLstEnc
** SYNOPSIS:    int
**                  PktLstEnc
**                  (
**                      asn_sck     *Asn,
**                      pkt_obj     *Lst,
**                      unsigned    LstLen
**                  )
** DESCRIPTION: Encodes a list of objects in ASN1.
** RETURNS:     0 -->   no error
**              else    error code
**************************************************************/
int PktLstEnc (Asn,Lst,LstLen)
asn_sck     *Asn;
pkt_obj     *Lst;
unsigned    LstLen;
{
    char
        *Eoc;

    if (AsnEocEnc (Asn, &Eoc) < 0)
        return (-1);
    Lst += LstLen;
    while (LstLen-- > 0)
    {
        if (PktObjEnc (Asn, --Lst) < 0)
            return (-1);
    }
    if (AsnHdrEnc (Asn, Eoc, ASN_UNI, ASN_CON, ASN_SEQ) < 0)
        return (-1);
    return (0);
}

/**************************************************************
** NAME:        PktLstDec
** SYNOPSIS:    int
**                  PktLstDec
**                  (
**                      asn_sck     *Asn,
**                      pkt_obj     *Lst,
**                      unsigned    LstSze,
**                      unsigned    *LstLen
**                  )
** DESCRIPTION: Decodes a list of objects from ASN1.
** RETURNS:     0 -->   no error
**              else    error code
**************************************************************/
int PktLstDec (Asn,Lst,LstSze,LstLen)
asn_sck     *Asn;
pkt_obj     *Lst;
unsigned    LstSze;
unsigned    *LstLen;
{
    unsigned
        Cls,
        Con,
        Tag;
    char
        *Eoc;

    if (AsnHdrDec (Asn, &Eoc, &Cls, &Con, &Tag) < 0)
        return (-1);
    if (Cls != ASN_UNI || Con != ASN_CON || Tag != ASN_SEQ)
        return (-1);
    *LstLen = 0;
    while (!AsnEoc (Asn, Eoc))
    {
        if (++(*LstLen) > LstSze)
            return (-1);
        if (PktObjDec (Asn, Lst++) < 0)
            return (-1);
    }
    if (AsnEocDec (Asn, Eoc) < 0)
        return (-1);
    return (0);
}

/**************************************************************
** NAME:        PktRorEnc
** SYNOPSIS:    int
**                  PktRorEnc
**                  (
**                      asn_sck     *Asn,
**                      pkt_ror     *Ror
**                  )
** DESCRIPTION: Encodes a request or response PDU in ASN1.
** RETURNS:     0 -->   no error
**              else    error code
**************************************************************/
int PktRorEnc (Asn,Ror)
asn_sck     *Asn;
pkt_ror     *Ror;
{
    char
        *End;

    if (PktLstEnc (Asn, Ror->RorLst, Ror->RorLstLen) < 0)
        return (-1);
    if (AsnIntEncUns (Asn, &End, Ror->RorErrInd) < 0)
        return (-1);
    if (AsnHdrEnc (Asn, End, ASN_UNI, ASN_PRI, ASN_INT) < 0)
        return (-1);
    if (AsnIntEncUns (Asn, &End, Ror->RorErrSts) < 0)
        return (-1);
    if (AsnHdrEnc (Asn, End, ASN_UNI, ASN_PRI, ASN_INT) < 0)
        return (-1);
    if (AsnIntEncLngUns (Asn, &End, Ror->RorRid) < 0)
        return (-1);
    if (AsnHdrEnc (Asn, End, ASN_UNI, ASN_PRI, ASN_INT) < 0)
        return (-1);
    return (0);
}

/**************************************************************
** NAME:        PktRorDec
** SYNOPSIS:    int
**                  PktRorDec
**                  (
**                      asn_sck     *Asn,
**                      pkt_ror     *Ror
**                  )
** DESCRIPTION: Decodes a request or response PDU from ASN1.
** RETURNS:     0 -->   no error
**              else    error code
**************************************************************/
int PktRorDec (Asn,Ror)
asn_sck     *Asn;
pkt_ror     *Ror;
{
    unsigned
        Cls,
        Con,
        Tag;
    char
        *End;

    if (AsnHdrDec (Asn, &End, &Cls, &Con, &Tag) < 0)
        return (-1);
    if (Cls != ASN_UNI || Con != ASN_PRI || Tag != ASN_INT)
        return (-1);
    if (AsnIntDecLngUns (Asn, End, &Ror->RorRid) < 0)
        return (-1);
    if (AsnHdrDec (Asn, &End, &Cls, &Con, &Tag) < 0)
        return (-1);
    if (Cls != ASN_UNI || Con != ASN_PRI || Tag != ASN_INT)
        return (-1);
    if (AsnIntDecUns (Asn, End, &Ror->RorErrSts) < 0)
        return (-1);
    if (AsnHdrDec (Asn, &End, &Cls, &Con, &Tag) < 0)
        return (-1);
    if (Cls != ASN_UNI || Con != ASN_PRI || Tag != ASN_INT)
        return (-1);
    if (AsnIntDecUns (Asn, End, &Ror->RorErrInd) < 0)
        return (-1);
    if (PktLstDec (Asn, Ror->RorLst, PKT_SZERORLST, &Ror->RorLstLen) < 0)
        return (-1);
    return (0);
}

/**************************************************************
** NAME:        PktTrpEnc
** SYNOPSIS:    int
**                  PktTrpEnc
**                  (
**                      asn_sck     *Asn,
**                      pkt_trp     *Trp
**                  )
** DESCRIPTION: Encodes a trap PDU in ASN1.
** RETURNS:     0 -->   no error
**              else    error code
**************************************************************/
int PktTrpEnc (Asn,Trp)
asn_sck     *Asn;
pkt_trp     *Trp;
{
    char
        *End;

    if (PktLstEnc (Asn, Trp->TrpLst, Trp->TrpLstLen) < 0)
        return (-1);
    if (AsnIntEncLngUns (Asn, &End, Trp->TrpTit) < 0)
        return (-1);
    if (AsnHdrEnc (Asn, End, ASN_APL, ASN_PRI, PKT_TIT) < 0)
        return (-1);
    if (AsnIntEncUns (Asn, &End, Trp->TrpSpe) < 0)
        return (-1);
    if (AsnHdrEnc (Asn, End, ASN_UNI, ASN_PRI, ASN_INT) < 0)
        return (-1);
    if (AsnIntEncUns (Asn, &End, Trp->TrpGen) < 0)
        return (-1);
    if (AsnHdrEnc (Asn, End, ASN_UNI, ASN_PRI, ASN_INT) < 0)
        return (-1);
    if (AsnOtsEnc (Asn, &End, (char *)&(Trp->TrpIpa), 4) < 0)
        return (-1);
    if (AsnHdrEnc (Asn, End, ASN_APL, ASN_PRI, PKT_IPA) < 0)
        return (-1);
    if (AsnOjiEnc (Asn, &End, Trp->TrpOji, Trp->TrpOjiLen) < 0)
        return (-1);
    if (AsnHdrEnc (Asn, End, ASN_UNI, ASN_PRI, ASN_OJI) < 0)
        return (-1);
    return (0);
}

/**************************************************************
** NAME:        PktTrpDec
** SYNOPSIS:    int
**                  PktTrpDec
**                  (
**                      asn_sck     *Asn,
**                      pkt_trp     *Trp
**                  )
** DESCRIPTION: Decodes a trap PDU from ASN1.
** RETURNS:     0 -->   no error
**              else    error code
**************************************************************/
int PktTrpDec (Asn,Trp)
asn_sck     *Asn;
pkt_trp     *Trp;
{
    unsigned
        Cls,
        Con,
        Tag;
    char
        *End;

    if (AsnHdrDec (Asn, &End, &Cls, &Con, &Tag) < 0)
        return (-1);
    if (Cls != ASN_UNI || Con != ASN_PRI || Tag != ASN_OJI)
        return (-1);
    if (AsnOjiDec (Asn, End, Trp->TrpOji, PKT_SZEOJI, &Trp->TrpOjiLen) < 0)
        return (-1);
    if (AsnHdrDec (Asn, &End, &Cls, &Con, &Tag) < 0)
        return (-1);
    if (Cls != ASN_APL || Con != ASN_PRI || Tag != PKT_IPA)
        return (-1);
    if (AsnOtsDec (Asn, End, (char *)&(Trp->TrpIpa), 4, &Tag) < 0)
        return (-1);
    if (AsnHdrDec (Asn, &End, &Cls, &Con, &Tag) < 0)
        return (-1);
    if (Cls != ASN_UNI || Con != ASN_PRI || Tag != ASN_INT)
        return (-1);
    if (AsnIntDecUns (Asn, End, &Trp->TrpGen) < 0)
        return (-1);
    if (AsnHdrDec (Asn, &End, &Cls, &Con, &Tag) < 0)
        return (-1);
    if (Cls != ASN_UNI || Con != ASN_PRI || Tag != ASN_INT)
        return (-1);
    if (AsnIntDecUns (Asn, End, &Trp->TrpSpe) < 0)
        return (-1);
    if (AsnHdrDec (Asn, &End, &Cls, &Con, &Tag) < 0)
        return (-1);
    if (Cls != ASN_APL || Con != ASN_PRI || Tag != PKT_TIT)
        return (-1);
    if (AsnIntDecLngUns (Asn, End, &Trp->TrpTit) < 0)
        return (-1);
    if (PktLstDec (Asn, Trp->TrpLst, PKT_SZETRPLST, &Trp->TrpLstLen) < 0)
        return (-1);
    return (0);
}

/**************************************************************
** NAME:        PktPduEnc
** SYNOPSIS:    int
**                  PktPduEnc
**                  (
**                      asn_sck     *Asn,
**                      pkt_pdu     *Pdu
**                  )
** DESCRIPTION: Encodes a PDU in ASN1.
**              Pdu is a union of pkt_ror and pkt_trp.
** RETURNS:     0 -->   no error
**              else    error code
**************************************************************/
int PktPduEnc (Asn,Pdu)
asn_sck     *Asn;
pkt_pdu     *Pdu;
{
    char
        *Eoc;

    if (AsnEocEnc (Asn, &Eoc) < 0)
        return (-1);
    switch (Pdu->PduTyp)
    {
        case PKT_GETRQS:
        case PKT_NXTRQS:
        case PKT_GETRSP:
        case PKT_SETRQS:
            if (PktRorEnc (Asn, &Pdu->PduRor) < 0)
                return (-1);
            break;
        case PKT_TRP:
           if (PktTrpEnc (Asn, &Pdu->PduTrp) < 0)
                return (-1);
            break;
        default:
            return (-1);
    }
    if (AsnHdrEnc (Asn, Eoc, ASN_CTX, ASN_CON, Pdu->PduTyp) < 0)
        return (-1);
    return (0);
}

/**************************************************************
** NAME:        PktPduDec
** SYNOPSIS:    int
**                  PktPduDec
**                  (
**                      asn_sck     *Asn,
**                      pkt_pdu     *Pdu
**                  )
** DESCRIPTION: Decodes a PDU from ASN1.
**              Pdu is a union of pkt_ror and pkt_trp.
** RETURNS:     0 -->   no error
**              else    error code
**************************************************************/
int PktPduDec (Asn,Pdu)
asn_sck     *Asn;
pkt_pdu     *Pdu;
{
    unsigned
        Cls,
        Con;
    char
        *Eoc;

    if (AsnHdrDec (Asn, &Eoc, &Cls, &Con, &Pdu->PduTyp) < 0)
        return (-1);
    if (Cls != ASN_CTX || Con != ASN_CON)
        return (-1);
    switch (Pdu->PduTyp)
    {
        case PKT_GETRQS:
        case PKT_NXTRQS:
        case PKT_GETRSP:
        case PKT_SETRQS:
            if (PktRorDec (Asn, &Pdu->PduRor) < 0)
                return (-1);
            break;
        case PKT_TRP:
            if (PktTrpDec (Asn, &Pdu->PduTrp) < 0)
                return (-1);
            break;
        default:
            PktSck.SckInBadTypes++;
            return (-1);
    }
    if (AsnEocDec (Asn, Eoc) < 0)
        return (-1);
    return (0);
}

/**************************************************************
** NAME:        PktEnc                                    [API]
** SYNOPSIS:    int
**                  PktEnc
**                  (
**                      long        Adr,
**                      pkt_msg     *Msg,
**                      char        **Pkt,
**                      unsigned    *PktLen
**                  )
** DESCRIPTION: Encodes a request, response or trap in ASN1.
**              Parameters:
**              Adr: IP destination address used by
**                   authentication service.
**              Msg: C structure of message to encode.
**              Pkt: Should initialy point to a char buffer
**                   which is used to encode the message.
**                   On return it points to the beginning of
**                   the encoding.
**              PktLen: Should point to an unsigned holding
**                      the length of the char buffer.
**                      On return this unsigned is set to the
**                      length of the encoding.
**              Because the encoding proceeds as a stack, the
**              beginning of the encoding is probably not equal
**              to the beginning of the char buffer.
** RETURNS:     0 -->        no error.
**              PKT_ERRASN   ASN parse error.
**************************************************************/
int PktEnc (Adr,Msg,Pkt,PktLen)
long        Adr;
pkt_msg     *Msg;
char        **Pkt;
unsigned    *PktLen;
{
    char
        *Eoc,
        *End;
    asn_sck
        Asn;

    AsnOpn (&Asn, *Pkt, *PktLen, ASN_ENC);
    if (AsnEocEnc (&Asn, &Eoc) < 0)
        return (PKT_ERRASN);
    if (PktPduEnc (&Asn, &Msg->MsgPdu) < 0)
        return (PKT_ERRASN);
    if (AutEnc (Msg->MsgCom, Msg->MsgComLen, Adr, &Asn) < 0)
        return (PKT_ERRAUT);
    if (AsnOtsEnc (&Asn, &End, Msg->MsgCom, Msg->MsgComLen) < 0)
        return (PKT_ERRASN);
    if (AsnHdrEnc (&Asn, End, ASN_UNI, ASN_PRI, ASN_OTS) < 0)
        return (PKT_ERRASN);
    if (AsnIntEncUns (&Asn, &End, PKT_VERSION) < 0)
        return (PKT_ERRASN);
    if (AsnHdrEnc (&Asn, End, ASN_UNI, ASN_PRI, ASN_INT) < 0)
        return (PKT_ERRASN);
    if (AsnHdrEnc (&Asn, Eoc, ASN_UNI, ASN_CON, ASN_SEQ) < 0)
        return (PKT_ERRASN);
    AsnCls (&Asn, Pkt, PktLen);
    PktSck.SckOutPkts++;
    switch (Msg->MsgPdu.PduTyp)
    {
        case PKT_GETRQS:
            PktSck.SckOutGetRequests++;
            break;
        case PKT_NXTRQS:
            PktSck.SckOutGetNexts++;
            break;
        case PKT_GETRSP:
            PktSck.SckOutGetResponses++;
            break;
        case PKT_SETRQS:
            PktSck.SckOutSetRequests++;
            break;
        case PKT_TRP:
            PktSck.SckOutTraps++;
            break;
    }
    return (PKT_ERROKY);
}

/**************************************************************
** NAME:        PktDec                                    [API]
** SYNOPSIS:    int
**                  PktDec
**                  (
**                      long        Adr,
**                      pkt_msg     *Msg,
**                      char        *Pkt,
**                      unsigned    PktLen
**                  )
** DESCRIPTION: Decodes a request, response or trap from ASN1.
**              Parameters:
**              Adr: IP source address used by
**                   authentication service.
**              Msg: C structure to decode message to.
**              Pkt: Should initialy point to the beginning
**                   of the encoding.
**              PktLen: Should hold the length of the encoding.
**              The decoded meesage can be a request, a
**              response or a trap. This should be checked
**              before using one of the different PDUs.
** RETURNS:     0 -->        no error
**              PKT_ERRASN   ASN parse error.
**              PKT_ERRAUT   Authentication failure.
**************************************************************/
int PktDec (Adr,Msg,Pkt,PktLen)
long        Adr;
pkt_msg     *Msg;
char        *Pkt;
unsigned    PktLen;
{
    unsigned
        Cls,
        Con,
        Tag;
    char
        *Eoc,
        *End;
    asn_sck
        Asn;
    unsigned
        Ver;

    AsnOpn (&Asn, Pkt, PktLen, ASN_DEC);
    if (AsnHdrDec (&Asn, &Eoc, &Cls, &Con, &Tag) < 0)
    {
        PktSck.SckInASNParseErrs++;
        return (PKT_ERRASN);
    }
    if (Cls != ASN_UNI || Con != ASN_CON || Tag != ASN_SEQ)
    {
        PktSck.SckInASNParseErrs++;
        return (PKT_ERRASN);
    }
    if (AsnHdrDec (&Asn, &End, &Cls, &Con, &Tag) < 0)
    {
        PktSck.SckInASNParseErrs++;
        return (PKT_ERRASN);
    }
    if (Cls != ASN_UNI || Con != ASN_PRI || Tag != ASN_INT)
    {
        PktSck.SckInASNParseErrs++;
        return (PKT_ERRASN);
    }
    if (AsnIntDecUns (&Asn, End, &Ver) < 0)
    {
        PktSck.SckInASNParseErrs++;
        return (PKT_ERRASN);
    }
    if (AsnHdrDec (&Asn, &End, &Cls, &Con, &Tag) < 0)
    {
        PktSck.SckInASNParseErrs++;
        return (PKT_ERRASN);
    }
    if (Cls != ASN_UNI || Con != ASN_PRI || Tag != ASN_OTS)
    {
        PktSck.SckInASNParseErrs++;
        return (PKT_ERRASN);
    }
    if (AsnOtsDec (&Asn, End, Msg->MsgCom, PKT_SZECOM, &Msg->MsgComLen) < 0)
    {
        PktSck.SckInASNParseErrs++;
        return (PKT_ERRASN);
    }
    if (Ver != PKT_VERSION)
    {
        PktSck.SckInBadVersions++;
        return (PKT_ERRASN);
    }
    if (AutDec (Msg->MsgCom, Msg->MsgComLen, Adr, &Asn) < 0)
        return (PKT_ERRAUT);
    if (PktPduDec (&Asn, &Msg->MsgPdu) < 0)
    {
        PktSck.SckInASNParseErrs++;
        return (PKT_ERRASN);
    }
    if (AsnEocDec (&Asn, Eoc) < 0)
    {
        PktSck.SckInASNParseErrs++;
        return (PKT_ERRASN);
    }
    AsnCls (&Asn, &Pkt, &PktLen);
    PktSck.SckInPkts++;
    switch (Msg->MsgPdu.PduTyp)
    {
        case PKT_GETRQS:
            PktSck.SckInGetRequests++;
            break;
        case PKT_NXTRQS:
            PktSck.SckInGetNexts++;
            break;
        case PKT_GETRSP:
            PktSck.SckInGetResponses++;
            break;
        case PKT_SETRQS:
            PktSck.SckInSetRequests++;
            break;
        case PKT_TRP:
            PktSck.SckInTraps++;
            break;
    }
    return (PKT_ERROKY);
}
