/* Copyright (c) 1992-1997 Microsoft Corporation Module Name: snmppdus.c Abstract: Contains routines for manipulating SNMP PDUs. Environment: User Mode - Win32 Revision History: 10-Feb-1997 DonRyan Rewrote to implement SNMPv2 support. */ /////////////////////////////////////////////////////////////////////////////// // // // Include files // // // /////////////////////////////////////////////////////////////////////////////// #include "globals.h" #include "trapthrd.h" // for doing authentication in this module #include "network.h" #include "snmpmgmt.h" #include "contexts.h" // for doing authentication in this module #include "snmpthrd.h" // for doing authentication in this module /////////////////////////////////////////////////////////////////////////////// // // // Private definitions // // // /////////////////////////////////////////////////////////////////////////////// #define BERERR ((LONG)-1) /////////////////////////////////////////////////////////////////////////////// // // // Private procedures // // // /////////////////////////////////////////////////////////////////////////////// LONG DoLenLen( LONG lLen ) /* Routine Description: Calculates number of bytes required to encode length. Arguments: lLen - length of interest. Return Values: Returns BERERR if unsuccessful. */ { // determine len length if (0x80 > lLen) return (1); if (0x100 > lLen) return (2); if (0x10000 > lLen) return (3); if (0x1000000 > lLen) return (4); SNMPDBG(( SNMP_LOG_ERROR, "SNMP: SVC: length field too large.\n" )); // failure return BERERR; } LONG FindLenInt( AsnInteger32 nValue ) /* Routine Description: Calculates length of integer. Arguments: nValue - integer data. Return Values: Returns BERERR if unsuccessful. */ { // negative? if (nValue < 0) { // determine length of negative int if ((ULONG)0x80 >= -nValue) return (1); if ((ULONG)0x8000 >= -nValue) return (2); if ((ULONG)0x800000 >= -nValue) return (3); } else { // determine length of positive int if ((ULONG)0x80 > nValue) return (1); if ((ULONG)0x8000 > nValue) return (2); if ((ULONG)0x800000 > nValue) return (3); } // default return (4); } LONG FindLenIntEx( AsnInteger32 nValue ) /* Routine Description: Calculates length of integer (including type and lenlen). Arguments: nValue - integer data. Return Values: Returns BERERR if unsuccessful. */ { // negative? if (nValue < 0) { // determine length of negative int if ((ULONG)0x80 >= -nValue) return (3); if ((ULONG)0x8000 >= -nValue) return (4); if ((ULONG)0x800000 >= -nValue) return (5); } else { // determine length of positive int if ((ULONG)0x80 > nValue) return (3); if ((ULONG)0x8000 > nValue) return (4); if ((ULONG)0x800000 > nValue) return (5); } // default return (6); } LONG FindLenUInt( AsnUnsigned32 nValue ) /* Routine Description: Calculates encoded length of unsigned integer. Arguments: nValue - integer data. Return Values: Returns BERERR if unsuccessful. */ { // determine length of unsigned int if ((ULONG)0x80 > nValue) return (1); if ((ULONG)0x8000 > nValue) return (2); if ((ULONG)0x800000 > nValue) return (3); if ((ULONG)0x80000000 > nValue) return (4); // default return (5); } LONG FindLenUIntEx( AsnUnsigned32 nValue ) /* Routine Description: Calculates encoded length of unsigned integer (including type and lenlen). Arguments: nValue - integer data. Return Values: Returns BERERR if unsuccessful. */ { // determine length of unsigned int if ((ULONG)0x80 > nValue) return (3); if ((ULONG)0x8000 > nValue) return (4); if ((ULONG)0x800000 > nValue) return (5); if ((ULONG)0x80000000 > nValue) return (6); // default return (7); } LONG FindLenCntr64( AsnCounter64 * pCntr64 ) /* Routine Description: Calculates encoded length of 64-bit counter. Arguments: pCntr64 - counter data. Return Values: Returns BERERR if unsuccessful. */ { // retrieve 64-bit unsigned value ULONGLONG nValue = pCntr64->QuadPart; // determine length of unsigned int if ((ULONGLONG)0x80 > nValue) return (1); if ((ULONGLONG)0x8000 > nValue) return (2); if ((ULONGLONG)0x800000 > nValue) return (3); if ((ULONGLONG)0x80000000 > nValue) return (4); if ((ULONGLONG)0x8000000000 > nValue) return (5); if ((ULONGLONG)0x800000000000 > nValue) return (6); if ((ULONGLONG)0x80000000000000 > nValue) return (7); if ((ULONGLONG)0x8000000000000000 > nValue) return (8); // default return (9); } LONG FindLenCntr64Ex( AsnCounter64 * pCntr64 ) /* Routine Description: Calculates encoded length of 64-bit counter (including type and lenlen). Arguments: pCntr64 - counter data. Return Values: Returns BERERR if unsuccessful. */ { // retrieve 64-bit unsigned value ULONGLONG nValue = pCntr64->QuadPart; // determine length of unsigned int if ((ULONGLONG)0x80 > nValue) return (3); if ((ULONGLONG)0x8000 > nValue) return (4); if ((ULONGLONG)0x800000 > nValue) return (5); if ((ULONGLONG)0x80000000 > nValue) return (6); if ((ULONGLONG)0x8000000000 > nValue) return (7); if ((ULONGLONG)0x800000000000 > nValue) return (8); if ((ULONGLONG)0x80000000000000 > nValue) return (9); if ((ULONGLONG)0x8000000000000000 > nValue) return (10); // default return (11); } LONG FindLenOctets( AsnOctetString * pOctets ) /* Routine Description: Calculates length of octet string. Arguments: pOctets - pointer to octet string. Return Values: Returns BERERR if unsuccessful. */ { // return size return pOctets->length; } LONG FindLenOctetsEx( AsnOctetString * pOctets ) /* Routine Description: Calculates length of octet string (including type and lenlen). Arguments: pOctets - pointer to octet string. Return Values: Returns BERERR if unsuccessful. */ { LONG lLenLen; // calculate bytes needed to encode lLenLen = DoLenLen(pOctets->length); // return total size return (lLenLen != BERERR) ? (pOctets->length + lLenLen + 1) : BERERR ; } LONG FindLenOid( AsnObjectIdentifier * pOid ) /* Routine Description: Calculates length of object identifier. Arguments: pOid - pointer object identifier. Return Values: Returns BERERR if unsuccessful. */ { UINT i; LONG lDataLen; // first two lDataLen = 1; // assume first two oids present for (i = 2; i < pOid->idLength; i++) { if (0x80 > pOid->ids[i]) { lDataLen += 1; } else if (0x4000 > pOid->ids[i]) { lDataLen += 2; } else if (0x200000 > pOid->ids[i]) { lDataLen += 3; } else if (0x10000000 > pOid->ids[i]) { lDataLen += 4; } else { lDataLen += 5; } } // return size return (pOid->idLength >= 2) ? lDataLen : BERERR; } LONG FindLenOidEx( AsnObjectIdentifier * pOid ) /* Routine Description: Calculates length of object identifier (including type and lenlen). Arguments: pOid - pointer object identifier. Return Values: Returns BERERR if unsuccessful. */ { UINT i; LONG lLenLen; LONG lDataLen; // first two lDataLen = 1; // assume first two oids present for (i = 2; i < pOid->idLength; i++) { if (0x80 > pOid->ids[i]) { lDataLen += 1; } else if (0x4000 > pOid->ids[i]) { lDataLen += 2; } else if (0x200000 > pOid->ids[i]) { lDataLen += 3; } else if (0x10000000 > pOid->ids[i]) { lDataLen += 4; } else { lDataLen += 5; } } // calculate len length lLenLen = DoLenLen(lDataLen); // return total size return ((lLenLen != BERERR) && (pOid->idLength >= 2)) ? (lDataLen + lLenLen + 1) : BERERR ; } LONG FindLenAsnAny( AsnAny * pAny ) /* Routine Description: Find length of variable binding value. Arguments: pAny - pointer to variable binding value. Return Values: Returns BERERR if unsuccessful. */ { // determine syntax switch (pAny->asnType) { case ASN_OCTETSTRING: case ASN_IPADDRESS: case ASN_OPAQUE: return FindLenOctets(&pAny->asnValue.string); case ASN_OBJECTIDENTIFIER: return FindLenOid(&pAny->asnValue.object); case ASN_NULL: case SNMP_EXCEPTION_NOSUCHOBJECT: case SNMP_EXCEPTION_NOSUCHINSTANCE: case SNMP_EXCEPTION_ENDOFMIBVIEW: return (0); case ASN_INTEGER32: return FindLenInt(pAny->asnValue.number); case ASN_COUNTER32: case ASN_GAUGE32: case ASN_TIMETICKS: case ASN_UNSIGNED32: return FindLenUInt(pAny->asnValue.unsigned32); case ASN_COUNTER64: return FindLenCntr64(&pAny->asnValue.counter64); } return BERERR; } LONG FindLenAsnAnyEx( AsnAny * pAny ) /* Routine Description: Find length of variable binding value (including type and lenlen). Arguments: pAny - pointer to variable binding value. Return Values: Returns BERERR if unsuccessful. */ { // determine syntax switch (pAny->asnType) { case ASN_OCTETSTRING: case ASN_IPADDRESS: case ASN_OPAQUE: return FindLenOctetsEx(&pAny->asnValue.string); case ASN_OBJECTIDENTIFIER: return FindLenOidEx(&pAny->asnValue.object); case ASN_NULL: case SNMP_EXCEPTION_NOSUCHOBJECT: case SNMP_EXCEPTION_NOSUCHINSTANCE: case SNMP_EXCEPTION_ENDOFMIBVIEW: return (2); case ASN_INTEGER32: return FindLenIntEx(pAny->asnValue.number); case ASN_COUNTER32: case ASN_GAUGE32: case ASN_TIMETICKS: case ASN_UNSIGNED32: return FindLenUIntEx(pAny->asnValue.unsigned32); case ASN_COUNTER64: return FindLenCntr64Ex(&pAny->asnValue.counter64); } return BERERR; } LONG FindLenVarBind( SnmpVarBind * pVb ) /* Routine Description: Find length of variable binding. Arguments: pVb - pointer to variable binding. Return Values: Returns BERERR if unsuccessful. */ { LONG lLenLen; LONG lOidLen; LONG lValueLen; // determine length of name lOidLen = FindLenOidEx(&pVb->name); // determine length of value lValueLen = FindLenAsnAnyEx(&pVb->value); // return total size return ((lOidLen != BERERR) && (lValueLen != BERERR)) ? (lOidLen + lValueLen) : BERERR ; } LONG FindLenVarBindEx( SnmpVarBind * pVb ) /* Routine Description: Find length of variable binding (including type and lenlen). Arguments: pVb - pointer to variable binding. Return Values: Returns BERERR if unsuccessful. */ { LONG lLenLen; LONG lOidLen; LONG lValueLen; // determine length of name lOidLen = FindLenOidEx(&pVb->name); // determine length of value lValueLen = FindLenAsnAnyEx(&pVb->value); // determine length of varbind length lLenLen = DoLenLen(lOidLen + lValueLen); // return total size return ((lLenLen != BERERR) && (lOidLen != BERERR) && (lValueLen != BERERR)) ? (lOidLen + lValueLen + lLenLen + 1) : BERERR ; } LONG FindLenVarBindList( SnmpVarBindList * pVbl ) /* Routine Description: Find length of variable binding list. Arguments: pVbl - pointer to variable binding list. Return Values: Returns BERERR if unsuccessful. */ { UINT i; LONG lVbLen = 0; LONG lVblLen = 0; // process each variable binding in the list for (i = 0; (lVbLen != BERERR) && (i < pVbl->len); i++) { // determine length of variable binding lVbLen = FindLenVarBindEx(&pVbl->list[i]); // add to total lVblLen += lVbLen; } // return total size return (lVbLen != BERERR) ? lVblLen : BERERR ; } LONG FindLenVarBindListEx( SnmpVarBindList * pVbl ) /* Routine Description: Find length of variable binding list (including type and lenlen). Arguments: pVbl - pointer to variable binding list. Return Values: Returns BERERR if unsuccessful. */ { UINT i; LONG lVbLen = 0; LONG lVblLen = 0; LONG lLenLen; // process each variable binding in the list for (i = 0; (lVbLen != BERERR) && (i < pVbl->len); i++) { // determine length of variable binding lVbLen = FindLenVarBindEx(&pVbl->list[i]); // add to total lVblLen += lVbLen; } // determine list length lLenLen = DoLenLen(lVblLen); // return total size return ((lVbLen != BERERR) && (lLenLen != BERERR)) ? (lVblLen + lLenLen + 1) : BERERR ; } VOID AddNull( LPBYTE * ppByte, INT nType ) /* Routine Description: Adds null into stream. Arguments: ppByte - pointer to pointer to current stream. nType - exact syntax. Return Values: None. */ { // encode actual syntax *(*ppByte)++ = (BYTE)(0xFF & nType); *(*ppByte)++ = 0x00; } VOID AddLen( LPBYTE * ppByte, LONG lLenLen, LONG lDataLen ) /* Routine Description: Adds data length field to current stream. Arguments: ppByte - pointer to pointer to current stream. lLenLen - length of data length. lDataLen - actual data length. Return Values: None. */ { LONG i; if (lLenLen == 1) { *(*ppByte)++ = (BYTE)lDataLen; } else { *(*ppByte)++ = (BYTE)(0x80 + lLenLen - 1); for (i = 1; i < lLenLen; i++) { *(*ppByte)++ = (BYTE)((lDataLen >> (8 * (lLenLen - i - 1))) & 0xFF); } } } LONG AddInt( LPBYTE * ppByte, INT nType, AsnInteger32 nInteger32 ) /* Routine Description: Adds integer to current stream. Arguments: ppByte - pointer to pointer to current stream. nType - exact syntax of integer. nInteger32 - actual data. Return Values: Returns BERERR if unsuccessful. */ { LONG i; LONG lDataLen; LONG lLenLen; // determine length of integer lDataLen = FindLenInt(nInteger32); // lenlen lLenLen = 1; // encode nType of integer *(*ppByte)++ = (BYTE)(0xFF & nType); // encode length of integer AddLen(ppByte, lLenLen, lDataLen); // add encoded integer for (i = 0; i < lDataLen; i++) { *(*ppByte)++ = (BYTE)(nInteger32 >> (8 * ((lDataLen - 1) - i) & 0xFF)); } return (0); } LONG AddUInt( LPBYTE * ppByte, INT nType, AsnUnsigned32 nUnsigned32 ) /* Routine Description: Adds unsigned integer to current stream. Arguments: ppByte - pointer to pointer to current stream. nType - exact syntax of integer. nUnsigned32 - actual data. Return Values: Returns BERERR if unsuccessful. */ { LONG i; LONG lDataLen; LONG lLenLen; // determine length of integer lDataLen = FindLenUInt(nUnsigned32); // < 127 octets lLenLen = 1; // encode actual syntax *(*ppByte)++ = (BYTE)(0xFF & nType); // encode data length AddLen(ppByte, lLenLen, lDataLen); // analyze length if (lDataLen == 5) { // put 00 in first octet *(*ppByte)++ = (BYTE)0; // encode unsigned integer for (i = 1; i < lDataLen; i++) { *(*ppByte)++ = (BYTE)(nUnsigned32 >> (8 * ((lDataLen - 1) - i) & 0xFF)); } } else { // encode unsigned integer for (i = 0; i < lDataLen; i++) { *(*ppByte)++ = (BYTE)(nUnsigned32 >> (8 * ((lDataLen - 1) - i) & 0xFF)); } } return (0); } LONG AddCntr64( LPBYTE * ppByte, INT nType, AsnCounter64 * pCntr64 ) /* Routine Description: Adds 64-bit counter to current stream. Arguments: ppByte - pointer to pointer to current stream. nType - exact syntax of counter. pCntr64 - actual data. Return Values: Returns BERERR if unsuccessful. */ { LONG i; LONG lDataLen; LONG lLenLen; // determine length of counter64 lDataLen = FindLenCntr64(pCntr64); // < 127 octets lLenLen = 1; // encode actual syntax *(*ppByte)++ = (BYTE)(0xFF & nType); // encode data length AddLen(ppByte, lLenLen, lDataLen); // adjust lDataLen if (lDataLen == 9) { // put 00 in first octet *(*ppByte)++ = (BYTE)0; lDataLen--; } // encode counter data for (i = lDataLen; i > 4; i--) { *(*ppByte)++ = (BYTE)(pCntr64->HighPart >> (8 * (i - 5) & 0xFF)); } for (; i > 0; i--) { *(*ppByte)++ = (BYTE)(pCntr64->LowPart >> (8 * (i - 1) & 0xFF)); } return (0); } LONG AddOctets( LPBYTE * ppByte, INT nType, AsnOctetString * pOctets ) /* Routine Description: Adds octet string to current stream. Arguments: ppByte - pointer to pointer to current stream. nType - exact syntax of string. pOctets - actual data. Return Values: Returns BERERR if unsuccessful. */ { UINT i; LONG lLenLen; LONG lDataLen; // determine oid length if ((lDataLen = FindLenOctets(pOctets)) == BERERR) return BERERR; // calculate octet string length if ((lLenLen = DoLenLen(lDataLen)) == BERERR) return BERERR; // encode actual syntax *(*ppByte)++ = (BYTE)(0xFF & nType); // encode octet string length AddLen(ppByte, lLenLen, lDataLen); // usless copy avoided if (*ppByte != pOctets->stream) { // encode actual octets for (i = 0; i < pOctets->length; i++) *(*ppByte)++ = pOctets->stream[i]; } else { (*ppByte) += pOctets->length; } return (0); } LONG AddOid( LPBYTE * ppByte, INT nType, AsnObjectIdentifier * pOid ) /* Routine Description: Adds object identifier to current stream. Arguments: ppByte - pointer to pointer to current stream. nType - exact syntax of object identifier. pOid - pointer to object identifier. Return Values: Returns BERERR if unsuccessful. */ { UINT i; LONG lLenLen = 0; LONG lDataLen; // determine oid length if ((lDataLen = FindLenOid(pOid)) == BERERR) return BERERR; // calculate number of bytes required for length if ((lLenLen = DoLenLen(lDataLen)) == BERERR) return BERERR; // add syntax to stream *(*ppByte)++ = (BYTE)(0xFF & nType); // add object identifier length AddLen(ppByte, lLenLen, lDataLen); // add first subid if (pOid->idLength < 2) *(*ppByte)++ = (BYTE)(pOid->ids[0] * 40); else *(*ppByte)++ = (BYTE)((pOid->ids[0] * 40) + pOid->ids[1]); // walk remaining subidentifiers for (i = 2; i < pOid->idLength; i++) { if (pOid->ids[i] < 0x80) { // 0 - 0x7f *(*ppByte)++ = (BYTE)pOid->ids[i]; } else if (pOid->ids[i] < 0x4000) { // 0x80 - 0x3fff *(*ppByte)++ = (BYTE) (((pOid->ids[i]) >> 7) | 0x80); // set high bit *(*ppByte)++ = (BYTE)(pOid->ids[i] & 0x7f); } else if (pOid->ids[i] < 0x200000) { // 0x4000 - 0x1FFFFF *(*ppByte)++ = (BYTE) (((pOid->ids[i]) >> 14) | 0x80); // set high bit *(*ppByte)++ = (BYTE) (((pOid->ids[i]) >> 7) | 0x80); // set high bit *(*ppByte)++ = (BYTE)(pOid->ids[i] & 0x7f); } else if (pOid->ids[i] < 0x10000000) { // 0x200000 - 0xFFfffff *(*ppByte)++ = (BYTE) (((pOid->ids[i]) >> 21) | 0x80); // set high bit *(*ppByte)++ = (BYTE) (((pOid->ids[i]) >> 14) | 0x80); // set high bit *(*ppByte)++ = (BYTE) (((pOid->ids[i]) >> 7) | 0x80); // set high bit *(*ppByte)++ = (BYTE)(pOid->ids[i] & 0x7f); } else { *(*ppByte)++ = (BYTE) (((pOid->ids[i]) >> 28) | 0x80); // set high bit *(*ppByte)++ = (BYTE) (((pOid->ids[i]) >> 21) | 0x80); // set high bit *(*ppByte)++ = (BYTE) (((pOid->ids[i]) >> 14) | 0x80); // set high bit *(*ppByte)++ = (BYTE) (((pOid->ids[i]) >> 7) | 0x80); // set high bit *(*ppByte)++ = (BYTE)(pOid->ids[i] & 0x7f); } } return (0); } LONG AddAsnAny( LPBYTE * ppByte, AsnAny * pAny ) /* Routine Description: Adds variable binding value to current stream. Arguments: ppByte - pointer to pointer to current stream. pAny - variable binding value. Return Values: Returns BERERR if unsuccessful. */ { // determine syntax switch (pAny->asnType) { case ASN_COUNTER32: case ASN_GAUGE32: case ASN_TIMETICKS: case ASN_UNSIGNED32: return AddUInt( ppByte, (INT)pAny->asnType, pAny->asnValue.unsigned32 ); case ASN_INTEGER32: return AddInt( ppByte, (INT)pAny->asnType, pAny->asnValue.number ); case ASN_OBJECTIDENTIFIER: return AddOid( ppByte, (INT)pAny->asnType, &pAny->asnValue.object ); case ASN_COUNTER64: return AddCntr64( ppByte, (INT)pAny->asnType, &pAny->asnValue.counter64 ); case ASN_OCTETSTRING: case ASN_IPADDRESS: case ASN_OPAQUE: return AddOctets( ppByte, (INT)pAny->asnType, &pAny->asnValue.string ); case ASN_NULL: case SNMP_EXCEPTION_NOSUCHOBJECT: case SNMP_EXCEPTION_NOSUCHINSTANCE: case SNMP_EXCEPTION_ENDOFMIBVIEW: AddNull(ppByte, (INT)pAny->asnType); return (0); } return BERERR; } LONG AddVarBind( LPBYTE * ppByte, SnmpVarBind * pVb ) /* Routine Description: Adds variable binding to current stream. Arguments: ppByte - pointer to pointer to current stream. pVb - pointer to variable binding. Return Values: Returns BERERR if unsuccessful. */ { LONG lLenLen; LONG lDataLen; // determine actual length of varbind data if ((lDataLen = FindLenVarBind(pVb)) == BERERR) return BERERR; // determine length of varbind data length if ((lLenLen = DoLenLen(lDataLen)) == BERERR) return BERERR; // encode as sequence *(*ppByte)++ = ASN_SEQUENCE; // encode data length AddLen(ppByte, lLenLen, lDataLen); // encode variable binding name if (AddOid(ppByte, ASN_OBJECTIDENTIFIER, &pVb->name) == BERERR) return BERERR; // encode variable binding value if (AddAsnAny(ppByte, &pVb->value) == BERERR) return BERERR; return (0); } LONG AddVarBindList( LPBYTE * ppByte, SnmpVarBindList * pVbl ) /* Routine Description: Adds variable binding list to current stream. Arguments: ppByte - pointer to pointer to current stream. pVbl - pointer to variable binding list. Return Values: Returns BERERR if unsuccessful. */ { UINT i; // add each variable binding for (i = 0; i < pVbl->len; i++) { if (AddVarBind(ppByte, &pVbl->list[i]) == BERERR) return BERERR; } return (0); } LONG ParseLength( LPBYTE * ppByte, LPBYTE pLastByte ) /* Routine Description: Parse length from current stream. Arguments: ppByte - pointer to pointer to current stream. pLastByte - pointer to end of current stream. Return Values: Returns BERERR if unsuccessful. */ { LONG i; LONG lLenLen; LONG lDataLen; if (*ppByte >= pLastByte) return BERERR; lDataLen = (LONG)*(*ppByte)++; if (lDataLen < 0x80) return (lDataLen); // check for long form lLenLen = lDataLen & 0x7f; // validate long form and bounds checking if ((lLenLen > 4) || (lLenLen < 1) || (lLenLen > (pLastByte - (*ppByte)))) return BERERR; lDataLen = 0L; for (i = 0; i < lLenLen; i++) { lDataLen = (lDataLen << 8) + *(*ppByte)++; } return (lDataLen); } LONG ParseType( LPBYTE * ppByte, LPBYTE pLastByte ) /* Routine Description: Parse type from current stream. Arguments: ppByte - pointer to pointer to current stream. pLastByte - pointer to end of current stream. Return Values: Returns BERERR if unsuccessful. */ { SHORT nType = BERERR; if (*ppByte >= pLastByte) return BERERR; nType = *(*ppByte)++; switch (nType) { case ASN_INTEGER32: case ASN_OCTETSTRING: case ASN_OBJECTIDENTIFIER: case ASN_SEQUENCE: case ASN_IPADDRESS: case ASN_COUNTER32: case ASN_GAUGE32: case ASN_TIMETICKS: case ASN_OPAQUE: case ASN_UNSIGNED32: case ASN_COUNTER64: case ASN_NULL: case SNMP_EXCEPTION_NOSUCHOBJECT: case SNMP_EXCEPTION_NOSUCHINSTANCE: case SNMP_EXCEPTION_ENDOFMIBVIEW: case SNMP_PDU_GET: case SNMP_PDU_GETNEXT: case SNMP_PDU_RESPONSE: case SNMP_PDU_SET: case SNMP_PDU_V1TRAP: case SNMP_PDU_GETBULK: case SNMP_PDU_INFORM: case SNMP_PDU_TRAP: break; default: nType = BERERR; break; } return (LONG)(SHORT)(nType); } BOOL ParseNull( LPBYTE * ppByte, LPBYTE pLastByte ) /* Routine Description: Parse null from current stream. Arguments: ppByte - pointer to pointer to current stream. pLastByte - pointer to end of current stream. Return Values: Returns FALSE if unsuccessful. */ { LONG lDataLen; if (ParseType(ppByte, pLastByte) == BERERR) return (FALSE); if ((lDataLen = ParseLength(ppByte, pLastByte)) == BERERR) return (FALSE); if (lDataLen != 0) return (FALSE); return (TRUE); } BOOL ParseSequence( LPBYTE * ppByte, LPBYTE pLastByte, LONG * plDataLen ) /* Routine Description: Parse sequence from current stream. Arguments: ppByte - pointer to pointer to current stream. pLastByte - pointer to end of current stream. plDataLen - pointer to receive sequence length. Return Values: Returns FALSE if unsuccessful. */ { LONG lDataLen; if ((ParseType(ppByte, pLastByte)) != ASN_SEQUENCE) return (FALSE); if ((lDataLen = ParseLength(ppByte, pLastByte)) == BERERR) return (FALSE); if (plDataLen) *plDataLen = lDataLen; return (TRUE); } BOOL ParseInt( LPBYTE * ppByte, LPBYTE pLastByte, AsnInteger32 * pInteger32 ) /* Routine Description: Parse integer from current stream. Arguments: ppByte - pointer to pointer to current stream. pLastByte - pointer to end of current stream. pInteger32 - pointer to receive integer. Return Values: Returns FALSE if unsuccessful. */ { LONG i; LONG lSign; LONG lDataLen; if (ParseType(ppByte, pLastByte) == BERERR) return (FALSE); if ((lDataLen = ParseLength(ppByte, pLastByte)) == BERERR) return (FALSE); if ((lDataLen <= 0) || (lDataLen > (pLastByte - (*ppByte)))) return (FALSE); if (lDataLen > 4) return (FALSE); lSign = ((*(*ppByte) & 0x80) == 0x00) ? 0x00 : 0xFF; *pInteger32 = 0; for (i = 0; i < lDataLen; i++) *pInteger32 = (*pInteger32 << 8) + (UINT)*(*ppByte)++; // sign-extend upper bits for (i = lDataLen; i < 4; i++) *pInteger32 = *pInteger32 + (lSign << i * 8); return (TRUE); } BOOL ParseUInt( LPBYTE * ppByte, LPBYTE pLastByte, AsnUnsigned32 * pUnsigned32 ) /* Routine Description: Parse unsigned integer from current stream. Arguments: ppByte - pointer to pointer to current stream. pLastByte - pointer to end of current stream. pUnsigned32 - pointer to receive integer. Return Values: Returns FALSE if unsuccessful. */ { LONG i; LONG lDataLen; if (ParseType(ppByte, pLastByte) == BERERR) return (FALSE); if ((lDataLen = ParseLength(ppByte, pLastByte)) == BERERR) return (FALSE); if ((lDataLen <= 0) || (lDataLen > (pLastByte - (*ppByte)))) return (FALSE); if ((lDataLen > 5) || ((lDataLen > 4) && (*(*ppByte) != 0x00))) return (FALSE); // leading null octet? if (*(*ppByte) == 0x00) { (*ppByte)++; // if so, skip it lDataLen--; // and don't count it } *pUnsigned32 = 0; for (i = 0; i < lDataLen; i++) *pUnsigned32 = (*pUnsigned32 << 8) + (UINT)*(*ppByte)++; return (TRUE); } BOOL ParseCntr64( LPBYTE * ppByte, LPBYTE pLastByte, AsnCounter64 * pCntr64 ) /* Routine Description: Parse 64-bit counter from current stream. Arguments: ppByte - pointer to pointer to current stream. pLastByte - pointer to end of current stream. pCntr64 - pointer to receive counter. Return Values: Returns FALSE if unsuccessful. */ { LONG i; LONG lDataLen; LONG nType; // initialize pCntr64->HighPart = 0L; pCntr64->LowPart = 0L; if ((nType = ParseType(ppByte, pLastByte)) == BERERR) return (FALSE); if (nType != ASN_COUNTER64) return (FALSE); if ((lDataLen = ParseLength(ppByte, pLastByte)) == BERERR) return (FALSE); if ((lDataLen <= 0) || (lDataLen > (pLastByte - (*ppByte)))) return (FALSE); if ((lDataLen > 9) || ((lDataLen > 8) && (*(*ppByte) != 0x00))) return (FALSE); // leading null octet? if (*(*ppByte) == 0x00) { (*ppByte)++; // if so, skip it lDataLen--; // and don't count it } for (i = 0; i < lDataLen; i++) { pCntr64->HighPart = (pCntr64->HighPart << 8) + (pCntr64->LowPart >> 24); pCntr64->LowPart = (pCntr64->LowPart << 8) + (unsigned long) *(*ppByte)++; } return TRUE; } BOOL ParseOctets( LPBYTE * ppByte, LPBYTE pLastByte, AsnOctetString * pOctets ) /* Routine Description: Parse octet string from current stream. Arguments: ppByte - pointer to pointer to current stream. pLastByte - pointer to end of current stream. pOctets - pointer to receive string. Return Values: Returns FALSE if unsuccessful. */ { LONG lDataLen; // initialize pOctets->length = 0; pOctets->stream = NULL; pOctets->dynamic = FALSE; if (ParseType(ppByte, pLastByte) == BERERR) return (FALSE); // make sure no conversion to UINT is done before testing // (pOctets->length is UINT) if ((lDataLen = ParseLength(ppByte, pLastByte)) == BERERR) return (FALSE); // note: we don't reject zero length Octet String if ((lDataLen < 0) || (lDataLen > (pLastByte - (*ppByte)))) return (FALSE); pOctets->length = (UINT)lDataLen; // validate length if (pOctets->length) { // point into buffer pOctets->stream = *ppByte; // WARNING! WARNING! } *ppByte += pOctets->length; return (TRUE); } BOOL ParseOid( LPBYTE * ppByte, LPBYTE pLastByte, AsnObjectIdentifier * pOid ) /* Routine Description: Parse object identifier from current stream. Arguments: ppByte - pointer to pointer to current stream. pLastByte - pointer to end of current stream. pOid - pointer to receive oid. Return Values: Returns FALSE if unsuccessful. */ { LONG i; LONG lDataLen; LONG nType; // initialize pOid->idLength = 0; pOid->ids = NULL; if ((nType = ParseType(ppByte, pLastByte)) == BERERR) return (FALSE); if (nType != ASN_OBJECTIDENTIFIER) return (FALSE); if ((lDataLen = ParseLength(ppByte, pLastByte)) == BERERR) return (FALSE); if (lDataLen <= 0) //--ft 03/02/98 removed trailing "|| lDataLen > SNMP_MAX_OID_LEN)" { // check is done in the while loop below SNMPDBG(( SNMP_LOG_ERROR, "SNMP: SVC: ParseOid: lDataLen <= 0, lDataLen=%d.\n", lDataLen )); return (FALSE); } // BUG# 486089 // the ((lDataLen + 2) * sizeof(UINT)) expression might cause overflow in // SnmpUtilMemAlloc below. adding one more check to limit its max. value. if ( lDataLen > (pLastByte - (*ppByte)) ) { SNMPDBG(( SNMP_LOG_ERROR, "SNMP: SVC: ParseOid: invalid lDataLen=%d, pLastByte=%p, *ppByte=%p.\n", lDataLen, pLastByte, *ppByte )); return (FALSE); } pOid->ids = SnmpUtilMemAlloc((DWORD)((lDataLen + 2) * sizeof(UINT))); if (pOid->ids == NULL) return (FALSE); // pOid->ids array space is pre-zero'd via SnmpUtilMemAlloc() while (lDataLen && (pOid->idLength < SNMP_MAX_OID_LEN)) { if (pOid->ids[pOid->idLength] & 0xFE000000) { // overflow in the next left shift SnmpUtilMemFree(pOid->ids); pOid->ids = NULL; pOid->idLength = 0; return (FALSE); } pOid->ids[pOid->idLength] = (pOid->ids[pOid->idLength] << 7) | (*(*ppByte) & 0x7F); if ((*(*ppByte)++ & 0x80) == 0) { // on the last octet of this sub-id if (pOid->idLength == 0) // check for first sub-id { // ASN.1/BER packs two into it pOid->ids[1] = pOid->ids[0]; pOid->ids[0] /= 40; if (pOid->ids[0] > 2) pOid->ids[0] = 2; pOid->ids[1] -= (pOid->ids[0] * 40); pOid->idLength++; // extra bump } pOid->idLength++; // increment the count on sub-id } lDataLen--; } // end_while (lDataLen) // BUG 506192 // Invalid OID BER of the form like "06 07 FF FF FF FF FF FF FF" // causes pOid->idLength becomes 0. Each subidentifier should be // encoded as a non-negative integer using as few 7-bit blocks as possible. // The blocks are packed in octets with the first bit of each octet equal // to 1 except for the last octet of each subidentifier. The example above // does not have the last octet. Added the (0 == pOid->idLength) test below. if (lDataLen || (0 == pOid->idLength)) { // the above while loop is terminated without finishing the parsing of the stream SnmpUtilMemFree(pOid->ids); pOid->ids = NULL; pOid->idLength = 0; return (FALSE); } return (TRUE); } BOOL ParseAsnAny( LPBYTE * ppByte, LPBYTE pLastByte, AsnAny * pAny ) /* Routine Description: Parse variable binding value from current stream. Arguments: ppByte - pointer to pointer to current stream. pLastByte - pointer to end of current stream. pAny - pointer to variable binding value. Return Values: Returns FALSE if unsuccessful. */ { // determine asn type switch (pAny->asnType) { case ASN_COUNTER32: case ASN_GAUGE32: case ASN_TIMETICKS: case ASN_UNSIGNED32: return ParseUInt( ppByte, pLastByte, &pAny->asnValue.unsigned32 ); case ASN_INTEGER32: return ParseInt( ppByte, pLastByte, &pAny->asnValue.number ); case ASN_OBJECTIDENTIFIER: return ParseOid( ppByte, pLastByte, &pAny->asnValue.object ); case ASN_COUNTER64: return ParseCntr64( ppByte, pLastByte, &pAny->asnValue.counter64 ); case ASN_OCTETSTRING: case ASN_IPADDRESS: case ASN_OPAQUE: return ParseOctets( ppByte, pLastByte, &pAny->asnValue.string ); case ASN_NULL: case SNMP_EXCEPTION_NOSUCHOBJECT: case SNMP_EXCEPTION_NOSUCHINSTANCE: case SNMP_EXCEPTION_ENDOFMIBVIEW: return ParseNull(ppByte, pLastByte); } return (FALSE); } BOOL ValidateContext( PNETWORK_LIST_ENTRY pNLE ) /*++ Routine Description: Checks access rights of given context. Arguments: pNLE - pointer to network list entry. Return Values: Returns true if manager allowed access. Also, pNLE->fAccessOk is TRUE if manager allowed access. --*/ { PCOMMUNITY_LIST_ENTRY pCLE = NULL; AsnOctetString unicodeCommunity; LPWSTR pUnicodeName; pNLE->fAccessOk = TRUE; if (pNLE->Community.length != 0) { unicodeCommunity.length = pNLE->Community.length * sizeof(WCHAR); unicodeCommunity.stream = SnmpUtilMemAlloc(unicodeCommunity.length); unicodeCommunity.dynamic = TRUE; if (unicodeCommunity.stream == NULL) { SNMPDBG(( SNMP_LOG_ERROR, "SNMP: SVC: ValidateContext: SnmpUtilMemAlloc failed to allocate %u bytes.\n", unicodeCommunity.length )); pNLE->fAccessOk = FALSE; return pNLE->fAccessOk; } pNLE->fAccessOk = (MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, pNLE->Community.stream, // lpMultiByteStr pNLE->Community.length, // cbMultiByte (LPWSTR)(unicodeCommunity.stream), // lpWideCharStr pNLE->Community.length) != 0); // cchWideChar if (!pNLE->fAccessOk) { SNMPDBG(( SNMP_LOG_ERROR, "SNMP: SVC: ValidateContext: MultiByteToWideChar returns 0 for request from community %s.\n", CommunityOctetsToString(&(pNLE->Community), FALSE) )); goto Error; } } else { unicodeCommunity.length = 0; unicodeCommunity.stream = NULL; unicodeCommunity.dynamic = FALSE; } // search for community string if (FindValidCommunity(&pCLE, &unicodeCommunity)) { // check access per pdu type if (pNLE->Pdu.nType == SNMP_PDU_SET) { // check flags for write privileges pNLE->fAccessOk = (pCLE->dwAccess >= SNMP_ACCESS_READ_WRITE); } else { // check flags for read privileges pNLE->fAccessOk = (pCLE->dwAccess >= SNMP_ACCESS_READ_ONLY); } if (!pNLE->fAccessOk) { // Community does not have the right access // register wrong operation for specified community into management structure mgmtCTick(CsnmpInBadCommunityUses); } } else { pNLE->fAccessOk = FALSE; // register community name failure into the management structure mgmtCTick(CsnmpInBadCommunityNames); } // see if access attempt should be logged if (!pNLE->fAccessOk && snmpMgmtBase.AsnIntegerPool[IsnmpEnableAuthenTraps].asnValue.number) { // send authentication trap GenerateAuthenticationTrap(); } Error: SNMPDBG(( SNMP_LOG_TRACE, "SNMP: SVC: %s request from community %s.\n", pNLE->fAccessOk ? "accepting" : "rejecting" , CommunityOctetsToString(&(pNLE->Community), FALSE) )); SnmpUtilOctetsFree(&unicodeCommunity); return (pNLE->fAccessOk); } BOOL ParseVarBind( LPBYTE * ppByte, LPBYTE pLastByte, SnmpVarBind * pVb ) /* Routine Description: Parse variable binding from current stream. Arguments: ppByte - pointer to pointer to current stream. pLastByte - pointer to end of current stream. pVb - pointer to variable binding. Return Values: Returns FALSE if unsuccessful. */ { if (!(ParseSequence(ppByte, pLastByte, NULL))) return (FALSE); if (!(ParseOid(ppByte, pLastByte, &pVb->name))) return (FALSE); if (*ppByte >= pLastByte) { // free memory allocated by ParseOid SnmpUtilOidFree(&pVb->name); return (FALSE); } pVb->value.asnType = (UINT)*(*ppByte); if (!(ParseAsnAny(ppByte, pLastByte, &pVb->value))) { // free memory allocated by ParseOid SnmpUtilOidFree(&pVb->name); return (FALSE); } return TRUE; } BOOL ParseVarBindList( LPBYTE * ppByte, LPBYTE pLastByte, SnmpVarBindList * pVbl ) /* Routine Description: Parse variable binding from current stream. Arguments: ppByte - pointer to pointer to current stream. pLastByte - pointer to end of current stream. pVbl - pointer to variable binding list. Return Values: Returns FALSE if unsuccessful. */ { SnmpVarBind Vb; SnmpVarBind * pVb = NULL; // initialize pVbl->list = NULL; pVbl->len = 0; // loop while data is left while (*ppByte < pLastByte) { if (!(ParseVarBind(ppByte, pLastByte, &Vb))) return (FALSE); // copy pointer pVb = pVbl->list; // attempt to allocate new variable binding pVb = SnmpUtilMemReAlloc(pVb, (pVbl->len + 1) * sizeof(SnmpVarBind)); // validate if (pVb == NULL) { SnmpUtilVarBindFree(&Vb); return FALSE; } // update varbind pVb[pVbl->len] = Vb; // update list pVbl->list = pVb; pVbl->len++; } return TRUE; } /////////////////////////////////////////////////////////////////////////////// // // // Public procedures (based on snmp\manager\winsnmp\dll\wsnmp_bn.c) // // // /////////////////////////////////////////////////////////////////////////////// BOOL BuildMessage( AsnInteger32 nVersion, AsnOctetString * pCommunity, PSNMP_PDU pPdu, PBYTE pMessage, PDWORD pMessageSize ) /* Routine Description: Builds outgoing SNMP PDU based on structure. Arguments: nVersion - SNMP version. pCommunity - pointer to community string. pPdu - pointer to PDU data structure. pMessage - pointer to buffer in which to build message. pMessageSize - pointer to receive size of message. Return Values: Returns true if successful. */ { LONG nVbDataLength; LONG nVbLenLength; LONG nVbTotalLength; LONG nPduDataLength; LONG nPduLenLength; LONG nPduTotalLength; LONG nMsgDataLength; LONG nMsgLenLength; LONG nMsgTotalLength; LONG nMsgAvailLength; LONG nTmpDataLength; LPBYTE tmpPtr = pMessage; // determine bytes available nMsgAvailLength = *pMessageSize; // find length of variable bindings list if ((nVbDataLength = FindLenVarBindList(&pPdu->Vbl)) == BERERR) return FALSE; // find length of length of variable bindings if ((nVbLenLength = DoLenLen(nVbDataLength)) == BERERR) return FALSE; // calculate total bytes required to encode varbinds nVbTotalLength = 1 + nVbLenLength + nVbDataLength; // determine pdu nType switch (pPdu->nType) { case SNMP_PDU_GET: case SNMP_PDU_GETNEXT: case SNMP_PDU_RESPONSE: case SNMP_PDU_SET: case SNMP_PDU_GETBULK: case SNMP_PDU_INFORM: case SNMP_PDU_TRAP: // calculate bytes required to encode pdu entries nPduDataLength = FindLenIntEx(pPdu->Pdu.NormPdu.nRequestId) + FindLenIntEx(pPdu->Pdu.NormPdu.nErrorStatus) + FindLenIntEx(pPdu->Pdu.NormPdu.nErrorIndex) + nVbTotalLength; break; case SNMP_PDU_V1TRAP: // calculate bytes required to encode pdu entries nPduDataLength = FindLenIntEx(pPdu->Pdu.TrapPdu.nGenericTrap) + FindLenIntEx(pPdu->Pdu.TrapPdu.nSpecificTrap) + FindLenUIntEx(pPdu->Pdu.TrapPdu.nTimeticks) + nVbTotalLength; // find oid length if ((nTmpDataLength = FindLenOidEx(&pPdu->Pdu.TrapPdu.EnterpriseOid)) == BERERR) return FALSE; // add EnterpriseOid oid length nPduDataLength += nTmpDataLength; // find address length if ((nTmpDataLength = FindLenOctetsEx(&pPdu->Pdu.TrapPdu.AgentAddr)) == BERERR) return FALSE; // add agent address length nPduDataLength += nTmpDataLength; break; default: return FALSE; } // find length of pdu length if ((nPduLenLength = DoLenLen(nPduDataLength)) == BERERR) return FALSE; // calculate total bytes required to encode pdu nPduTotalLength = 1 + nPduLenLength + nPduDataLength; // find community string length if ((nTmpDataLength = FindLenOctetsEx(pCommunity)) == BERERR) return FALSE; // find length of message data nMsgDataLength = FindLenUIntEx(nVersion) + nTmpDataLength + nPduTotalLength; // find length of message data length if ((nTmpDataLength = DoLenLen(nMsgDataLength)) == BERERR) return FALSE; nMsgLenLength = nTmpDataLength; // calculate total bytes required to encode message nMsgTotalLength = 1 + nMsgLenLength + nMsgDataLength; // record bytes required *pMessageSize = nMsgTotalLength; // make sure message fits in buffer if (nMsgTotalLength <= nMsgAvailLength) { LONG oldLength; // the length of the request PDU LONG delta; // difference between the request PDU length and the responce PDU length BYTE *newStream;// new location for the community stream inside the response PDU. // encode message as asn sequence *tmpPtr++ = ASN_SEQUENCE; // the pointer to the community string points either directly in the incoming buffer // (for req PDUs) or in the TRAP_DESTINATION_LIST_ENTRY for the outgoing traps. // In the first case, when building the outgoing message on the same buffer as the // incoming message, we need to take care not to overwrite the community name (in case // the length field is larger than for the initial message). Hence, in this case only // we shift the community name with a few octets, as many as the difference between the // encodings of the two lengths (the length of the outgoing response - the length of the // incoming request). if (pPdu->nType != SNMP_PDU_V1TRAP) { // here tmpPtr points exactly to the length of the request pdu oldLength = (LONG)(*tmpPtr); // bug# 176433 // compute the offset the community stream should be shifted with delta = nMsgLenLength - ((oldLength & 0x80) ? (oldLength & 0x7f) + 1 : 1); if (delta > 0) { // move memory in case the response nMsgLenLength > oldLength newStream = pCommunity->stream + delta; // pCommunity->stream is shifted regardles memory regions overlapp memmove(newStream, pCommunity->stream, pCommunity->length); // make old community to point to the new location pCommunity->stream = newStream; } } // encode global message information AddLen(&tmpPtr, nMsgLenLength, nMsgDataLength); AddUInt(&tmpPtr, ASN_INTEGER32, nVersion); if (AddOctets(&tmpPtr, ASN_OCTETSTRING, pCommunity) == BERERR) return (FALSE); // encode pdu header information *tmpPtr++ = (BYTE)pPdu->nType; AddLen(&tmpPtr, nPduLenLength, nPduDataLength); // determine pdu nType switch (pPdu->nType) { case SNMP_PDU_RESPONSE: case SNMP_PDU_TRAP: AddInt(&tmpPtr, ASN_INTEGER32, pPdu->Pdu.NormPdu.nRequestId); AddInt(&tmpPtr, ASN_INTEGER32, pPdu->Pdu.NormPdu.nErrorStatus); AddInt(&tmpPtr, ASN_INTEGER32, pPdu->Pdu.NormPdu.nErrorIndex); break; case SNMP_PDU_V1TRAP: if (AddOid( &tmpPtr, ASN_OBJECTIDENTIFIER, &pPdu->Pdu.TrapPdu.EnterpriseOid)== BERERR) return FALSE; if (AddOctets( &tmpPtr, ASN_IPADDRESS, &pPdu->Pdu.TrapPdu.AgentAddr) == BERERR) return FALSE; AddInt(&tmpPtr, ASN_INTEGER32, pPdu->Pdu.TrapPdu.nGenericTrap); AddInt(&tmpPtr, ASN_INTEGER32, pPdu->Pdu.TrapPdu.nSpecificTrap); AddUInt(&tmpPtr, ASN_TIMETICKS, pPdu->Pdu.TrapPdu.nTimeticks); break; case SNMP_PDU_GET: case SNMP_PDU_GETNEXT: case SNMP_PDU_SET: case SNMP_PDU_INFORM: case SNMP_PDU_GETBULK: default: return FALSE; } // encode variable bindings *tmpPtr++ = ASN_SEQUENCE; AddLen(&tmpPtr, nVbLenLength, nVbDataLength); if (AddVarBindList(&tmpPtr, &pPdu->Vbl) == BERERR) return FALSE; // success return TRUE; } // failure return FALSE; } BOOL ParseMessage( PNETWORK_LIST_ENTRY pNLE ) /* Routine Description: Parses incoming SNMP PDU into structure. Arguments: pNLE - pointer to network list entry. Return Values: Returns true if successful. Note: When TRUE is returned, pNLE->nVersion, pNLE->Community, pNLE->Pdu are updated with structure data parsed from pNLE->Buffer.buf. When FALSE is returned AND pNLE->fAccessOk is FALSE, the failure is due to authentication instead of ASN parsing errors. */ { LONG lLength; LPBYTE pByte; LPBYTE pLastByte; AsnInteger32 * pVersion = &pNLE->nVersion; // pointer to receive SNMP version. AsnOctetString * pCommunity = &pNLE->Community; // pointer to receive community string. PSNMP_PDU pPdu = &pNLE->Pdu; // pointer to receive remaining PDU data. PBYTE pMessage = pNLE->Buffer.buf; // pointer to message to parse. DWORD dwMessageSize = pNLE->dwBytesTransferred; // number of bytes in message. // initialize authentication to a successful state pNLE->fAccessOk = TRUE; // initialize community pCommunity->stream = NULL; pCommunity->length = 0; // initialize vbl pPdu->Vbl.len = 0; pPdu->Vbl.list = NULL; // validate pointer if (!(pByte = pMessage)) goto cleanup; // set limit based on packet size pLastByte = pByte + dwMessageSize; // decode asn sequence message wrapper if (!(ParseSequence(&pByte, pLastByte, &lLength))) goto cleanup; // check for packet fragments if ( (lLength <= 0) || (lLength > (pLastByte - pByte)) ) goto cleanup; // re-adjust based on data pLastByte = pByte + lLength; // decode snmp version if (!(ParseUInt(&pByte, pLastByte, pVersion))) goto cleanup; // validate snmp version if ((*pVersion != SNMP_VERSION_1) && (*pVersion != SNMP_VERSION_2C)) { // register version mismatch into the management structure mgmtCTick(CsnmpInBadVersions); goto cleanup; } // decode community string if (!(ParseOctets(&pByte, pLastByte, pCommunity))) goto cleanup; // decode nType of incoming pdu if ((pPdu->nType = ParseType(&pByte, pLastByte)) == BERERR) goto cleanup; // decode length of incoming pdu if ((lLength = ParseLength(&pByte, pLastByte)) == BERERR) goto cleanup; // validate length if ( (lLength <= 0) || (lLength > (pLastByte - pByte)) ) goto cleanup; // BUG# 552295 validate context before parsing the PDU switch (pPdu->nType) { case SNMP_PDU_GET: case SNMP_PDU_GETNEXT: case SNMP_PDU_SET: case SNMP_PDU_GETBULK: if (!ValidateContext(pNLE)) { goto cleanup; } break; case SNMP_PDU_INFORM: case SNMP_PDU_RESPONSE: case SNMP_PDU_TRAP: case SNMP_PDU_V1TRAP: default: goto cleanup; } // determine pdu nType switch (pPdu->nType) { case SNMP_PDU_GET: case SNMP_PDU_GETNEXT: case SNMP_PDU_SET: // decode the pdu header information if (!(ParseInt(&pByte, pLastByte, &pPdu->Pdu.NormPdu.nRequestId))) goto cleanup; if (!(ParseInt(&pByte, pLastByte, &pPdu->Pdu.NormPdu.nErrorStatus))) goto cleanup; if (!(ParseInt(&pByte, pLastByte, &pPdu->Pdu.NormPdu.nErrorIndex))) goto cleanup; // update the management counters for the incoming errorStatus coding mgmtUtilUpdateErrStatus(IN_errStatus, pPdu->Pdu.NormPdu.nErrorStatus); // no reason here to have any ErrorStatus and ErrorIndex. // initialize error status variables to NOERROR pPdu->Pdu.NormPdu.nErrorStatus = SNMP_ERRORSTATUS_NOERROR; pPdu->Pdu.NormPdu.nErrorIndex = 0; break; case SNMP_PDU_GETBULK: // decode the getbulk pdu header information if (!(ParseInt(&pByte, pLastByte, &pPdu->Pdu.BulkPdu.nRequestId))) goto cleanup; if (!(ParseInt(&pByte, pLastByte, &pPdu->Pdu.BulkPdu.nNonRepeaters))) goto cleanup; if (!(ParseInt(&pByte, pLastByte, &pPdu->Pdu.BulkPdu.nMaxRepetitions))) goto cleanup; // see if value needs to be adjusted if (pPdu->Pdu.BulkPdu.nNonRepeaters < 0) { // adjust non-repeaters to zero pPdu->Pdu.BulkPdu.nNonRepeaters = 0; } // see if value needs to be adjusted if (pPdu->Pdu.BulkPdu.nMaxRepetitions < 0) { // adjust max-repetitions to zero pPdu->Pdu.BulkPdu.nMaxRepetitions = 0; } // initialize status information pPdu->Pdu.BulkPdu.nErrorStatus = SNMP_ERRORSTATUS_NOERROR; pPdu->Pdu.BulkPdu.nErrorIndex = 0; break; case SNMP_PDU_INFORM: case SNMP_PDU_RESPONSE: case SNMP_PDU_TRAP: case SNMP_PDU_V1TRAP: default: goto cleanup; } // parse over sequence if (!(ParseSequence(&pByte, pLastByte, NULL))) goto cleanup; // parse variable binding list if (!(ParseVarBindList(&pByte, pLastByte, &pPdu->Vbl))) goto cleanup; // success return TRUE; cleanup: // cleanup community string SnmpUtilOctetsFree(pCommunity); // cleanup any allocated varbinds SnmpUtilVarBindListFree(&pPdu->Vbl); // failure return FALSE; }