// wsnmp_bn.c // // WinSNMP Low-Level SNMP/ASN.1/BER Functions and helpers // Copyright 1995-1997 ACE*COMM Corp // Rleased to Microsoft under Contract // Beta 1 version, 970228 // Bob Natale (bnatale@acecomm.com) // // 980424 - Received msgLen may be larger than pduLen // - ParsePduHdr() and ParseMessage() now accommodate this. // 980420 - Mods related to ParseCntr64() inspired by // MS bug ID 127357 (removal of temp64 variable) // - Mod to ParseOID() for MS bug ID 127353 // (reset os_ptr->ptr to NULL on error) // // 970310 - Typographical changes // #include "winsnmp.inc" long FindLenVarBind (LPVARBIND vb_ptr); long FindLenVALUE (smiLPVALUE); long FindLenOctetString (smiLPOCTETS os_ptr); long FindLenOID (smiLPCOID oid_ptr); long FindLenUInt (smiUINT32 value); long FindLenInt (smiINT32 value); long FindLenCntr64 (smiLPCNTR64 value); long DoLenLen (smiINT32 len); void AddLen (smiLPBYTE *tmpPtr, smiINT32 lenlen, smiINT32 data_len); long AddVarBind (smiLPBYTE *tmpPtr, LPVARBIND vb_ptr); long AddOctetString (smiLPBYTE *tmpPtr, int type, smiLPOCTETS os_ptr); long AddOID (smiLPBYTE *tmpPtr, smiLPOID oid_ptr); long AddUInt (smiLPBYTE *tmpPtr, int type, smiUINT32 value); long AddInt (smiLPBYTE *tmpPtr, smiINT32 value); long AddCntr64 (smiLPBYTE *tmpPtr, smiLPCNTR64 value); void AddNull (smiLPBYTE *tmpPtr, int type); LPVARBIND ParseVarBind (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen); BOOL ParseOctetString (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen, smiLPOCTETS os_ptr); BOOL ParseOID (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen, smiLPOID oid_ptr); BOOL ParseCntr64 (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen, smiLPCNTR64 cntr64_ptr); BOOL ParseUInt (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen, smiLPUINT32 value); BOOL ParseInt (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen, smiLPINT value); BOOL ParseNull (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen); BOOL ParseSequence (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen); smiINT32 ParseType (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen); smiINT32 ParseLength (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen); void FreeOctetString (smiLPOCTETS os_ptr) { if (os_ptr) { if (os_ptr->ptr) GlobalFree (os_ptr->ptr); GlobalFree (os_ptr); } return; } void FreeVarBindList (LPVARBIND vb_ptr) { if (vb_ptr) { // NULLs are handled by downstream call FreeVarBindList (vb_ptr->next_var); FreeVarBind (vb_ptr); } return; } void FreeVarBind (LPVARBIND vb_ptr) { if (vb_ptr) { if (vb_ptr->name.ptr) GlobalFree (vb_ptr->name.ptr); switch (vb_ptr->value.syntax) { case SNMP_SYNTAX_OID: if (vb_ptr->value.value.oid.ptr) GlobalFree (vb_ptr->value.value.oid.ptr); break; case SNMP_SYNTAX_OCTETS: case SNMP_SYNTAX_IPADDR: case SNMP_SYNTAX_OPAQUE: if (vb_ptr->value.value.string.ptr) GlobalFree (vb_ptr->value.value.string.ptr); break; default: // Remaining types do not have 'ptr' members break; } // end_switch GlobalFree (vb_ptr); } // end_if (vb_ptr) return; } // end_FreeVarBind void FreeV1Trap (LPV1TRAP v1Trap_ptr) { if (v1Trap_ptr) { if (v1Trap_ptr->enterprise.ptr) GlobalFree (v1Trap_ptr->enterprise.ptr); if (v1Trap_ptr->agent_addr.ptr) GlobalFree (v1Trap_ptr->agent_addr.ptr); GlobalFree (v1Trap_ptr); } } // end_FreeV1Trap void AddLen (smiLPBYTE *tmpPtr, long lenlen, long data_len) { long i; if (lenlen == 1) *(*tmpPtr)++ = (smiBYTE)data_len; else { *(*tmpPtr)++ = (smiBYTE)(0x80 + lenlen - 1); for (i = 1; i < lenlen; i++) { *(*tmpPtr)++ = (smiBYTE)((data_len >> (8 * (lenlen - i - 1))) & 0xFF); } // end_for } // end_else return; } // end_AddLen long AddVarBind (smiLPBYTE *tmpPtr, LPVARBIND vb_ptr) { long lenlen; if (vb_ptr == NULL) return (0); if ((lenlen = DoLenLen(vb_ptr->data_length)) == -1) return (-1); *(*tmpPtr)++ = SNMP_SYNTAX_SEQUENCE; AddLen (tmpPtr, lenlen, vb_ptr->data_length); if (AddOID (tmpPtr, &vb_ptr->name) == -1) return (-1); switch (vb_ptr->value.syntax) { case SNMP_SYNTAX_CNTR32: case SNMP_SYNTAX_GAUGE32: case SNMP_SYNTAX_TIMETICKS: case SNMP_SYNTAX_UINT32: AddUInt (tmpPtr, (int)vb_ptr->value.syntax, vb_ptr->value.value.uNumber); break; case SNMP_SYNTAX_INT: AddInt (tmpPtr, vb_ptr->value.value.sNumber); break; case SNMP_SYNTAX_OID: if (AddOID (tmpPtr, (smiLPOID)&(vb_ptr->value.value.oid)) == -1) return (-1); break; case SNMP_SYNTAX_CNTR64: AddCntr64 (tmpPtr, (smiLPCNTR64)&(vb_ptr->value.value.hNumber)); break; case SNMP_SYNTAX_OCTETS: case SNMP_SYNTAX_IPADDR: case SNMP_SYNTAX_OPAQUE: if (AddOctetString (tmpPtr, (int)vb_ptr->value.syntax, (smiLPOCTETS)&(vb_ptr->value.value.string)) == -1) return -1; break; case SNMP_SYNTAX_NULL: case SNMP_SYNTAX_NOSUCHOBJECT: case SNMP_SYNTAX_NOSUCHINSTANCE: case SNMP_SYNTAX_ENDOFMIBVIEW: AddNull (tmpPtr, (int)vb_ptr->value.syntax); break; default: return (-1); } // end_switch return (AddVarBind (tmpPtr, vb_ptr->next_var)); } long AddOctetString (smiLPBYTE *tmpPtr, int type, smiLPOCTETS os_ptr) { UINT i; long lenlen; if ((lenlen = DoLenLen ((long)os_ptr->len)) == -1) return (-1); *(*tmpPtr)++ = (smiBYTE)(0xFF & type); AddLen (tmpPtr, lenlen, os_ptr->len); for (i = 0; i < os_ptr->len; i++) *(*tmpPtr)++ = os_ptr->ptr[i]; return (0); } long AddOID (smiLPBYTE *tmpPtr, smiLPOID oid_ptr) { UINT i; long lenlen = 0; long encoded_len; encoded_len = 1; // for first two SID's for (i = 2; i < oid_ptr->len; i++) { if (oid_ptr->ptr[i] < 0x80) // 0 - 0x7F encoded_len += 1; else if (oid_ptr->ptr[i] < 0x4000) // 0x80 - 0x3FFF encoded_len += 2; else if (oid_ptr->ptr[i] < 0x200000) // 0x4000 - 0x1FFFFF encoded_len += 3; else if (oid_ptr->ptr[i] < 0x10000000) // 0x200000 - 0xFFFFFFF encoded_len += 4; else encoded_len += 5; } if ((lenlen = DoLenLen (encoded_len)) == -1) return (-1); *(*tmpPtr)++ = (smiBYTE)(0xFF & SNMP_SYNTAX_OID); AddLen (tmpPtr, lenlen, encoded_len); if (oid_ptr->len < 2) *(*tmpPtr)++ = (smiBYTE)(oid_ptr->ptr[0] * 40); else *(*tmpPtr)++ = (smiBYTE)((oid_ptr->ptr[0] * 40) + oid_ptr->ptr[1]); for (i = 2; i < oid_ptr->len; i++) { if (oid_ptr->ptr[i] < 0x80) { // 0 - 0x7F *(*tmpPtr)++ = (smiBYTE)oid_ptr->ptr[i]; } else if (oid_ptr->ptr[i] < 0x4000) { // 0x80 - 0x3FFF *(*tmpPtr)++ = (smiBYTE) (((oid_ptr->ptr[i]) >> 7) | 0x80); // set high bit *(*tmpPtr)++ = (smiBYTE)(oid_ptr->ptr[i] & 0x7f); } else if (oid_ptr->ptr[i] < 0x200000) { // 0x4000 - 0x1FFFFF *(*tmpPtr)++ = (smiBYTE) (((oid_ptr->ptr[i]) >> 14) | 0x80); // set high bit *(*tmpPtr)++ = (smiBYTE) (((oid_ptr->ptr[i]) >> 7) | 0x80); // set high bit *(*tmpPtr)++ = (smiBYTE)(oid_ptr->ptr[i] & 0x7f); } else if (oid_ptr->ptr[i] < 0x10000000) { // 0x200000 - 0xFFFFFFF *(*tmpPtr)++ = (smiBYTE) (((oid_ptr->ptr[i]) >> 21) | 0x80); // set high bit *(*tmpPtr)++ = (smiBYTE) (((oid_ptr->ptr[i]) >> 14) | 0x80); // set high bit *(*tmpPtr)++ = (smiBYTE) (((oid_ptr->ptr[i]) >> 7) | 0x80); // set high bit *(*tmpPtr)++ = (smiBYTE)(oid_ptr->ptr[i] & 0x7f); } else { *(*tmpPtr)++ = (smiBYTE) (((oid_ptr->ptr[i]) >> 28) | 0x80); // set high bit *(*tmpPtr)++ = (smiBYTE) (((oid_ptr->ptr[i]) >> 21) | 0x80); // set high bit *(*tmpPtr)++ = (smiBYTE) (((oid_ptr->ptr[i]) >> 14) | 0x80); // set high bit *(*tmpPtr)++ = (smiBYTE) (((oid_ptr->ptr[i]) >> 7) | 0x80); // set high bit *(*tmpPtr)++ = (smiBYTE)(oid_ptr->ptr[i] & 0x7f); } } // end_for return (0); } // end_AddOID long AddUInt (smiLPBYTE *tmpPtr, int type, smiUINT32 value) { long i; long datalen; long lenlen; // if high bit one, must use 5 octets (first with 00) if (((value >> 24) & 0xFF) != 0) datalen = 4; else if (((value >> 16) & 0xFF) != 0) datalen = 3; else if (((value >> 8) & 0xFF) != 0) datalen = 2; else datalen = 1; if (((value >> (8 * (datalen - 1))) & 0x80) != 0) datalen++; lenlen = 1; // < 127 octets *(*tmpPtr)++ = (smiBYTE)(0xFF & type); AddLen(tmpPtr, lenlen, datalen); if (datalen == 5) { // gotta put a 00 in first octet *(*tmpPtr)++ = (smiBYTE)0; for (i = 1; i < datalen; i++) { *(*tmpPtr)++ = (smiBYTE)(value >> (8 * ((datalen - 1) - i) & 0xFF)); } } // end_if else { for (i = 0; i < datalen; i++) { *(*tmpPtr)++ = (smiBYTE)(value >> (8 * ((datalen - 1) - i) & 0xFF)); } } // end_else return (0); } // end_AddUInt long AddInt (smiLPBYTE *tmpPtr, smiINT32 value) { long i; long datalen; long lenlen; switch ((smiBYTE) ((value >> 24) & 0xFF)) { case 0x00: if (((value >> 16) & 0xFF) != 0) datalen = 3; else if (((value >> 8) & 0xFF) != 0) datalen = 2; else datalen = 1; if (((value >> (8 * (datalen - 1))) & 0x80) != 0) datalen++; break; case 0xFF: if (((value >> 16) & 0xFF) != 0xFF) datalen = 3; else if (((value >> 8) & 0xFF) != 0xFF) datalen = 2; else datalen = 1; if (((value >> (8 * (datalen - 1))) & 0x80) == 0) datalen++; break; default: datalen = 4; } // end_switch lenlen = 1; // < 127 octets *(*tmpPtr)++ = (smiBYTE)(0xFF & SNMP_SYNTAX_INT); AddLen(tmpPtr, lenlen, datalen); for (i = 0; i < datalen; i++) { *(*tmpPtr)++ = (smiBYTE) (value >> (8 * ((datalen - 1) - i) & 0xFF)); } return (0); } // end_AddInt() long AddCntr64 (smiLPBYTE *tmpPtr, smiLPCNTR64 value) { long i; long datalen; long lenlen; datalen = FindLenCntr64(value) - 2; lenlen = 1; // < 127 octets *(*tmpPtr)++ = (smiBYTE)(0xFF & SNMP_SYNTAX_CNTR64); AddLen(tmpPtr, lenlen, datalen); if (datalen == 9) { // gotta put a 00 in first octet *(*tmpPtr)++ = (smiBYTE)0; datalen--; } for (i = datalen; i > 4; i--) { *(*tmpPtr)++ = (smiBYTE)(value->hipart >> (8 * (i - 5) & 0xFF)); } for (; i > 0; i--) { *(*tmpPtr)++ = (smiBYTE)(value->lopart >> (8 * (i - 1) & 0xFF)); } return (0); } long FindLenVarBind (LPVARBIND vb_ptr) { long lenlen; long tot_so_far; long lOidLen; long lValueLen; if (!vb_ptr) return (0); tot_so_far = FindLenVarBind (vb_ptr->next_var); if (tot_so_far == -1) return (-1); if ((lOidLen = FindLenOID (&vb_ptr->name)) == -1) return (-1); if ((lValueLen = FindLenVALUE (&vb_ptr->value)) == -1) return (-1); vb_ptr->data_length = lOidLen + lValueLen; if ((lenlen = DoLenLen (vb_ptr->data_length)) == -1) return (-1); return (1 + lenlen + vb_ptr->data_length + tot_so_far); } // end_FindLenVarBind long FindLenVALUE (smiLPVALUE value_ptr) { if (value_ptr) { switch (value_ptr->syntax) { case SNMP_SYNTAX_OCTETS: case SNMP_SYNTAX_IPADDR: case SNMP_SYNTAX_OPAQUE: return (FindLenOctetString (&value_ptr->value.string)); case SNMP_SYNTAX_OID: return (FindLenOID (&value_ptr->value.oid)); case SNMP_SYNTAX_NULL: case SNMP_SYNTAX_NOSUCHOBJECT: case SNMP_SYNTAX_NOSUCHINSTANCE: case SNMP_SYNTAX_ENDOFMIBVIEW: return (2); case SNMP_SYNTAX_INT: return (FindLenInt (value_ptr->value.sNumber)); case SNMP_SYNTAX_CNTR32: case SNMP_SYNTAX_GAUGE32: case SNMP_SYNTAX_TIMETICKS: case SNMP_SYNTAX_UINT32: return (FindLenUInt (value_ptr->value.uNumber)); case SNMP_SYNTAX_CNTR64: return (FindLenCntr64 (&value_ptr->value.hNumber)); } // end_switch } // end_if return (-1); } // end_FindLenVALUE long FindLenOctetString (smiLPOCTETS os_ptr) { long lenlen; if (!os_ptr) return (-1); if ((lenlen = DoLenLen (os_ptr->len)) == -1) return (-1); return (1 + lenlen + os_ptr->len); } long FindLenOID (smiLPCOID oid_ptr) { long lenlen; UINT i; UINT encoded_len; encoded_len = 1; // for first two Sub-IDs // beware of i = 2 for (i = 2; i < oid_ptr->len; i++) { if (oid_ptr->ptr[i] < 0x80) // 0 - 0x7F encoded_len += 1; else if (oid_ptr->ptr[i] < 0x4000) // 0x80 - 0x3FFF encoded_len += 2; else if (oid_ptr->ptr[i] < 0x200000) // 0x4000 - 0x1FFFFF encoded_len += 3; else if (oid_ptr->ptr[i] < 0x10000000) // 0x200000 - 0xFFFFFFF encoded_len += 4; else encoded_len += 5; } // end_for if ((lenlen = DoLenLen (encoded_len)) == -1) return (-1); return (1 + lenlen + encoded_len); } // end_FindLenOID long FindLenUInt (smiUINT32 value) { long datalen; // if high bit one, must use 5 octets (first with 00) if (((value >> 24) & 0xFF) != 0) datalen = 4; else if (((value >> 16) & 0xFF) != 0) datalen = 3; else if (((value >> 8) & 0xFF) != 0) datalen = 2; else datalen = 1; if (((value >> (8 * (datalen - 1))) & 0x80) != 0) datalen++; // length of length < 127 octets return (1 + 1 + datalen); } long FindLenInt (smiINT32 value) { long datalen; switch ((smiBYTE) ((value >> 24) & 0xFF)) { case 0x00: if (((value >> 16) & 0xFF) != 0) datalen = 3; else if (((value >> 8) & 0xFF) != 0) datalen = 2; else datalen = 1; if (((value >> (8 * (datalen - 1))) & 0x80) != 0) datalen++; break; case 0xFF: if (((value >> 16) & 0xFF) != 0xFF) datalen = 3; else if (((value >> 8) & 0xFF) != 0xFF) datalen = 2; else datalen = 1; if (((value >> (8 * (datalen - 1))) & 0x80) == 0) datalen++; break; default: datalen = 4; } // end_switch return (1 + 1 + datalen); } long FindLenCntr64 (smiLPCNTR64 value) { long datalen; // if high bit one, must use 5 octets (first with 00) if (((value->hipart >> 24) & 0xFF) != 0) { datalen = 8; if (((value->hipart >> 24) & 0x80) != 0) datalen++; } else if (((value->hipart >> 16) & 0xFF) != 0) { datalen = 7; if (((value->hipart >> 16) & 0x80) != 0) datalen++; } else if (((value->hipart >> 8) & 0xFF) != 0) { datalen = 6; if (((value->hipart >> 8) & 0x80) != 0) datalen++; } else if (((value->hipart) & 0xFF) != 0) { datalen = 5; if (((value->hipart) & 0x80) != 0) datalen++; } else if (((value->lopart>> 24) & 0xFF) != 0) { datalen = 4; if (((value->lopart >> 24) & 0x80) != 0) datalen++; } else if (((value->lopart >> 16) & 0xFF) != 0) { datalen = 3; if (((value->lopart >> 16) & 0x80) != 0) datalen++; } else if (((value->lopart >> 8) & 0xFF) != 0) { datalen = 2; if (((value->lopart >> 8) & 0x80) != 0) datalen++; } else { datalen = 1; if (((value->lopart) & 0x80) != 0) datalen++; } // length of length < 127 octets return (1 + 1 + datalen); } long DoLenLen (long len) { // short form? if (len < 128) return (1); if (len < 0x100) return (2); if (len < 0x10000) return (3); if (len < 0x1000000) return (4); return (-1); } void AddNull (smiLPBYTE *tmpPtr, int type) { *(*tmpPtr)++ = (smiBYTE)(0xFF & type); *(*tmpPtr)++ = 0x00; return; } BOOL BuildMessage (smiUINT32 version, smiLPOCTETS community, LPPDUS pdu, smiINT32 requestId, smiLPBYTE *msgAddr, smiLPUINT32 msgSize) { LPVARBIND vbList = NULL; long nVbDataLen, nVbLenLen, nVbTotalLen; long nPduDataLen, nPduLenLen, nPduTotalLen; long nMsgDataLen, nMsgLenLen, nMsgTotalLen; long nTmpDataLen; smiLPBYTE tmpPtr = NULL; *msgAddr = NULL; *msgSize = 0; if (pdu == NULL || community == NULL) return (FALSE); // Determine length of VarBind list part vbList = pdu->VBL_addr; if (vbList == NULL && pdu->VBL != 0) vbList = ((LPVBLS)snmpGetTableEntry(&VBLsDescr, HandleToUlong(pdu->VBL)-1))->vbList; // vbList == NULL is ok if ((nVbDataLen = FindLenVarBind (vbList)) == -1) return (FALSE); if ((nVbLenLen = DoLenLen (nVbDataLen)) == -1) return (FALSE); nVbTotalLen = 1 + nVbLenLen + nVbDataLen; // Determine length of PDU overhead part switch (pdu->type) { 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: nPduDataLen = FindLenInt (requestId) + FindLenInt (pdu->errStatus) + FindLenInt (pdu->errIndex) + nVbTotalLen; break; case SNMP_PDU_V1TRAP: if (!pdu->v1Trap) return (FALSE); nPduDataLen = FindLenInt (pdu->v1Trap->generic_trap) + FindLenInt (pdu->v1Trap->specific_trap) + FindLenUInt (pdu->v1Trap->time_ticks) + nVbTotalLen; if ((nTmpDataLen = FindLenOID (&pdu->v1Trap->enterprise)) == -1) return (FALSE); nPduDataLen += nTmpDataLen; if ((nTmpDataLen = FindLenOctetString (&pdu->v1Trap->agent_addr)) == -1) return (FALSE); nPduDataLen += nTmpDataLen; break; default: return (FALSE); } // end_switch if ((nPduLenLen = DoLenLen(nPduDataLen)) == -1) return (FALSE); nPduTotalLen = 1 + nPduLenLen + nPduDataLen; if ((nTmpDataLen = FindLenOctetString (community)) == -1) return (FALSE); nMsgDataLen = FindLenUInt (version) + nTmpDataLen + nPduTotalLen; if ((nMsgLenLen = DoLenLen (nMsgDataLen)) == -1) return (FALSE); nMsgTotalLen = 1 + nMsgLenLen + nMsgDataLen; // Allocate the necessary memory for the message tmpPtr = GlobalAlloc (GPTR, nMsgTotalLen); if (tmpPtr == NULL) return (FALSE); *msgAddr = tmpPtr; *msgSize = nMsgTotalLen; // Now plug in the values in the message bytes *tmpPtr++ = SNMP_SYNTAX_SEQUENCE; // Wrapper portion AddLen (&tmpPtr, nMsgLenLen, nMsgDataLen); AddInt (&tmpPtr, version); if (AddOctetString (&tmpPtr, SNMP_SYNTAX_OCTETS, community) == -1) goto error_out; // PDU header portion // "Downgrade" GetBulk to GetNext if target is SNMPv1 if (pdu->type == SNMP_PDU_GETBULK && version == 0) *tmpPtr++ = SNMP_PDU_GETNEXT; else *tmpPtr++ = (BYTE) pdu->type; AddLen (&tmpPtr, nPduLenLen, nPduDataLen); switch (pdu->type) { case SNMP_PDU_GET: case SNMP_PDU_GETNEXT: case SNMP_PDU_RESPONSE: case SNMP_PDU_SET: case SNMP_PDU_INFORM: case SNMP_PDU_TRAP: case SNMP_PDU_GETBULK: AddInt (&tmpPtr, requestId); AddInt (&tmpPtr, pdu->errStatus); AddInt (&tmpPtr, pdu->errIndex); break; case SNMP_PDU_V1TRAP: if (AddOID (&tmpPtr, &pdu->v1Trap->enterprise)== -1) goto error_out; if (AddOctetString (&tmpPtr, SNMP_SYNTAX_IPADDR, &pdu->v1Trap->agent_addr) == -1) goto error_out; AddInt (&tmpPtr, pdu->v1Trap->generic_trap); AddInt (&tmpPtr, pdu->v1Trap->specific_trap); AddUInt (&tmpPtr, SNMP_SYNTAX_TIMETICKS, pdu->v1Trap->time_ticks); break; default: goto error_out; } // end_switch // VarBindList portion *tmpPtr++ = SNMP_SYNTAX_SEQUENCE; AddLen (&tmpPtr, nVbLenLen, nVbDataLen); if (AddVarBind (&tmpPtr, vbList) == -1) { error_out: if (*msgAddr) GlobalFree (*msgAddr); *msgAddr = NULL; *msgSize = 0; return (FALSE); } // Success return (TRUE); } // end_BuildMessage() BOOL SetPduType (smiLPBYTE msgPtr, smiUINT32 msgLen, int pduType) { smiLPBYTE tmpPtr; smiINT32 tmp; if (!(tmpPtr = msgPtr)) // Deliberate assignment return (FALSE); if (!(ParseSequence (&tmpPtr, &msgLen))) // sequence return (FALSE); if (!(ParseUInt (&tmpPtr, &msgLen, &tmp))) // version return (FALSE); // Jump over communityString...not needed here if (ParseType (&tmpPtr, &msgLen) == -1) return (FALSE); // -1 is error, we also reject 0 and -ve length commuityString if ((tmp = ParseLength (&tmpPtr, &msgLen)) <= 0) return (FALSE); if ((smiUINT32)tmp > msgLen) return (FALSE); tmpPtr += (smiUINT32)tmp; // Jump! // Set the PDU type byte *tmpPtr = (smiBYTE)pduType; return (TRUE); } // End_SetPduType() smiUINT32 ParsePduHdr (smiLPBYTE msgPtr, smiUINT32 msgLen, smiLPUINT32 version, smiLPINT32 type, smiLPUINT32 reqID) { // This is a private function (not exported via WinSNMP) // It is called only once by msgNotify() (another private function) // to "peek ahead" at certain PDU attributes to determine the next // procesing steps. smiINT32 pduLen; smiINT32 length; long errcode = 1; if (msgPtr == NULL) goto DONE; errcode++; // 2 // Parse initial Sequence field... if (ParseType (&msgPtr, &msgLen) != SNMP_SYNTAX_SEQUENCE) goto DONE; errcode++; // 3 // ...to get the remaining pduLen out of it pduLen = ParseLength (&msgPtr, &msgLen); if (pduLen <= 0) goto DONE; errcode++; // 4 if ((smiUINT32)pduLen > msgLen) goto DONE; errcode++; // 5 msgLen = (smiUINT32)pduLen; // Only pduLen counts now if (!(ParseUInt (&msgPtr, &msgLen, version))) goto DONE; errcode++; // 6 // Jump over communityString...not needed here if (ParseType (&msgPtr, &msgLen) == -1) goto DONE; errcode++; // 7 // -1 is error, we also reject 0 and -ve length commuityString if ((length = ParseLength (&msgPtr, &msgLen)) <= 0) goto DONE; errcode++; // 8 if ((smiUINT32)length > msgLen) goto DONE; errcode++; // 9 msgPtr += (smiUINT32)length; // Jump! msgLen -= (smiUINT32)length; // Get PDU type if ((*type = ParseType (&msgPtr, &msgLen)) == -1) goto DONE; errcode++; // 10 // Check PDU type for requestID semantics if (*type == SNMP_PDU_V1TRAP) *reqID = 0; // No requestID on v1 trapPDU else // Not a v1 trapPDU, therefore { // must get requestID // -1 is error, reject 0 and any -ve values too. if ((ParseLength (&msgPtr, &msgLen)) <= 0) goto DONE; errcode++; // 11 if (!(ParseInt (&msgPtr, &msgLen, reqID))) goto DONE; } errcode = 0; DONE: return (errcode); } // end_ParsePduHdr smiUINT32 ParseMessage (smiLPBYTE msgPtr, smiUINT32 msgLen, smiLPUINT32 version, smiLPOCTETS *community, LPPDUS pdu) { smiINT32 pduLen; smiLPOCTETS os_ptr; LPVARBIND vb_ptr; LPVARBIND vb_end_ptr; long errcode = 1; if (msgPtr == NULL) goto DONE; errcode++; // 2 // Parse initial Sequence field... if (ParseType (&msgPtr, &msgLen) != SNMP_SYNTAX_SEQUENCE) goto DONE; errcode++; // 3 // ...to get the remaining pduLen out of it pduLen = ParseLength (&msgPtr, &msgLen); if (pduLen <= 0) goto DONE; errcode++; // 4 if ((smiUINT32)pduLen > msgLen) goto DONE; errcode++; // 5 msgLen = (smiUINT32)pduLen; // Only pduLen counts now if (!(ParseUInt (&msgPtr, &msgLen, version))) goto DONE; errcode++; // 5 if (*version != 0 && *version != 1) // SNMPv1 or SNMPv2c goto DONE; errcode++; // 6 if (!(os_ptr = GlobalAlloc (GPTR, sizeof(smiOCTETS)))) goto DONE; errcode++; // 7 if (!(ParseOctetString (&msgPtr, &msgLen, os_ptr))) goto DONE_OS; // reject 0 length community string if (os_ptr->len == 0) goto DONE_OS; errcode++; // 8 if (pdu == NULL) goto DONE_OS; ZeroMemory (pdu, sizeof(PDUS)); if ((pdu->type = ParseType (&msgPtr, &msgLen)) == -1) goto DONE_PDU; errcode++; // 9 pduLen = ParseLength (&msgPtr, &msgLen); if ((pduLen <= 0) || (smiUINT32)pduLen > msgLen) goto DONE_PDU; errcode++; // 10 switch (pdu->type) { 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: if (!(ParseInt (&msgPtr, &msgLen, &pdu->appReqId))) goto DONE_PDU; errcode++; // 11 if (!(ParseInt (&msgPtr, &msgLen, &pdu->errStatus))) goto DONE_PDU; errcode++; // 12 if (!(ParseInt (&msgPtr, &msgLen, &pdu->errIndex))) goto DONE_PDU; errcode++; // 13 break; case SNMP_PDU_V1TRAP: pdu->v1Trap = GlobalAlloc (GPTR, sizeof(V1TRAP)); if (pdu->v1Trap == NULL) goto DONE_PDU; errcode++; // 11 if (!(ParseOID (&msgPtr, &msgLen, &pdu->v1Trap->enterprise))) goto DONE_PDU; errcode++; // 12 if (!(ParseOctetString (&msgPtr, &msgLen, &pdu->v1Trap->agent_addr))) goto DONE_PDU; errcode++; // 13 if (!(ParseInt (&msgPtr, &msgLen, &pdu->v1Trap->generic_trap))) goto DONE_PDU; errcode++; // 14 if (!(ParseInt (&msgPtr, &msgLen, &pdu->v1Trap->specific_trap))) goto DONE_PDU; errcode++; // 15 if (!(ParseUInt (&msgPtr, &msgLen, &pdu->v1Trap->time_ticks))) goto DONE_PDU; errcode++; // 16 break; default: goto DONE_PDU; } // end_switch errcode = 20; // re-normalize // Waste the SEQUENCE tag if (!(ParseSequence (&msgPtr, &msgLen))) goto DONE_PDU; errcode++; // 21 // Parse the varbind list pdu->VBL = 0; pdu->VBL_addr = NULL; while (msgLen) { if (!(vb_ptr = ParseVarBind (&msgPtr, &msgLen))) goto DONE_PDU; errcode++; // 22+ if (!pdu->VBL_addr) // Is this the first one? vb_end_ptr = pdu->VBL_addr = vb_ptr; // If so, start a list else { // tack onto end of list vb_end_ptr->next_var = vb_ptr; vb_end_ptr = vb_ptr; } } // end_while errcode = 0; *community = os_ptr; goto DONE; DONE_PDU: FreeVarBindList (pdu->VBL_addr); // Checks for NULL FreeV1Trap (pdu->v1Trap); // Checks for NULL ZeroMemory (pdu, sizeof(PDUS)); DONE_OS: FreeOctetString (os_ptr); DONE: return (errcode); } // end_ParseMessage LPVARBIND ParseVarBind (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen) { LPVARBIND vb_ptr; if (!(ParseSequence (tmpPtr, tmpLen))) return (NULL); if ((vb_ptr = (LPVARBIND)GlobalAlloc(GPTR, sizeof(VARBIND))) == NULL) return (NULL); if (!(ParseOID(tmpPtr, tmpLen, &vb_ptr->name))) goto ERROROUT; //we're going to derefrence (*tmpPtr), check length left first if (*tmpLen == 0) goto ERROROUT; vb_ptr->value.syntax = (smiUINT32)*(*tmpPtr); switch (vb_ptr->value.syntax) { case SNMP_SYNTAX_CNTR32: case SNMP_SYNTAX_GAUGE32: case SNMP_SYNTAX_TIMETICKS: case SNMP_SYNTAX_UINT32: if (!(ParseUInt (tmpPtr, tmpLen, &vb_ptr->value.value.uNumber))) goto ERROROUT; break; case SNMP_SYNTAX_INT: if (!(ParseInt (tmpPtr, tmpLen, &vb_ptr->value.value.sNumber))) goto ERROROUT; break; case SNMP_SYNTAX_OID: if (!(ParseOID (tmpPtr, tmpLen, &vb_ptr->value.value.oid))) goto ERROROUT; break; case SNMP_SYNTAX_CNTR64: if (!(ParseCntr64 (tmpPtr, tmpLen, &vb_ptr->value.value.hNumber))) goto ERROROUT; break; case SNMP_SYNTAX_OCTETS: case SNMP_SYNTAX_IPADDR: case SNMP_SYNTAX_OPAQUE: if (!(ParseOctetString (tmpPtr, tmpLen, &vb_ptr->value.value.string))) goto ERROROUT; break; case SNMP_SYNTAX_NULL: case SNMP_SYNTAX_NOSUCHOBJECT: case SNMP_SYNTAX_NOSUCHINSTANCE: case SNMP_SYNTAX_ENDOFMIBVIEW: if (!(ParseNull (tmpPtr, tmpLen))) goto ERROROUT; break; default: goto ERROROUT; } // end_switch return (vb_ptr); // Success // ERROROUT: FreeVarBind(vb_ptr); return (NULL); // Failure } // end_ParseVarBind BOOL ParseOctetString (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen, smiLPOCTETS os_ptr) { smiINT32 length; if (!os_ptr) return (FALSE); os_ptr->ptr = NULL; os_ptr->len = 0; if (ParseType (tmpPtr, tmpLen) == -1) return (FALSE); // make sure no conversion to UINT is done before testing // because os_ptr->len is of UINT type length = ParseLength (tmpPtr, tmpLen); // note: we don't reject zero length Octet String if (length < 0 || (smiUINT32)length > *tmpLen) return (FALSE); os_ptr->len = (smiUINT32)length; if (os_ptr->len) { // Does not allocate "string" space on "length = 0" if (!(os_ptr->ptr = (smiLPBYTE)GlobalAlloc (GPTR, os_ptr->len))) return (FALSE); CopyMemory (os_ptr->ptr, *tmpPtr, os_ptr->len); } *tmpPtr += os_ptr->len; *tmpLen -= os_ptr->len; return (TRUE); } // end_ParseOctetString BOOL ParseOID (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen, smiLPOID oid_ptr) { smiINT32 length; if (!oid_ptr) return (FALSE); oid_ptr->ptr = NULL; oid_ptr->len = 0; if (ParseType (tmpPtr, tmpLen) != SNMP_SYNTAX_OID) return (FALSE); length = ParseLength (tmpPtr, tmpLen); // -1 is error return from ParseLength() // BUG# 347175 this is just the length in bytes for the BER encoded OID in the stream, // this code should be the same as in %sdxroot%\net\snmp\newagent\exe\snmppdus.c!ParseOid. // removed the (|| length > MAXOBJIDSIZE) condition from the following test. It should be // moved to the while loop to test the number of sub-ids instead of bytes in stream. if (length <= 0) return (FALSE); if ((smiUINT32)length > *tmpLen) return (FALSE); // the sub-id array will by 1 longer than the ASN.1/BER array oid_ptr->ptr = (smiLPUINT32)GlobalAlloc (GPTR, sizeof(smiUINT32) * (length+1)); if (oid_ptr->ptr == NULL) return (FALSE); // oid_ptr structure space is pre-zero'd via GlobalAlloc() while (length && (oid_ptr->len < MAXOBJIDSIZE)) { if (oid_ptr->ptr[oid_ptr->len] & 0xFE000000) { // overflow in the next left shift GlobalFree(oid_ptr->ptr); oid_ptr->ptr = NULL; oid_ptr->len = 0; return (FALSE); } oid_ptr->ptr[oid_ptr->len] = (oid_ptr->ptr[oid_ptr->len] << 7) + (*(*tmpPtr) & 0x7F); if ((*(*tmpPtr)++ & 0x80) == 0) { // on the last octet of this sub-id if (oid_ptr->len == 0) // check for first sub-id { // ASN.1/BER packs two into it oid_ptr->ptr[1] = oid_ptr->ptr[0]; oid_ptr->ptr[0] /= 40; if (oid_ptr->ptr[0] > 2) oid_ptr->ptr[0] = 2; oid_ptr->ptr[1] -= (oid_ptr->ptr[0] * 40); oid_ptr->len++; // extra bump } oid_ptr->len++; // increment the count on sub-id } length--; (*tmpLen)--; } // end_while (length) // BUG 506192 // Invalid OID BER of the form like "06 07 FF FF FF FF FF FF FF" // causes oid_ptr->len 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 == oid_ptr->len) test below. if (length || (0 == oid_ptr->len)) { // the above while loop is terminated without finishing the parsing of the stream GlobalFree(oid_ptr->ptr); oid_ptr->ptr = NULL; oid_ptr->len = 0; return (FALSE); } return (TRUE); } // end_ParseOID BOOL ParseCntr64 (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen, smiLPCNTR64 Cntr64_ptr) { smiINT32 i; smiINT32 length; if (ParseType (tmpPtr, tmpLen) != SNMP_SYNTAX_CNTR64) return (FALSE); length = ParseLength(tmpPtr, tmpLen); if (length <= 0) return (FALSE); if ((smiUINT32)length > *tmpLen || length > 9 || (length == 9 && *(*tmpPtr) != 0x00)) return (FALSE); while (length && *(*tmpPtr) == 0x00) { // leading null octet? (*tmpPtr)++; // if so, skip it length--; // and don't count it (*tmpLen)--; // Adjust remaining msg length } Cntr64_ptr->hipart = Cntr64_ptr->lopart = 0; for (i = 0; i < length; i++) { Cntr64_ptr->hipart = (Cntr64_ptr->hipart << 8) + (Cntr64_ptr->lopart >> 24); Cntr64_ptr->lopart = (Cntr64_ptr->lopart << 8) + (smiUINT32) *(*tmpPtr)++; } *tmpLen -= length; return (TRUE); } // end_ParseCntr64 BOOL ParseUInt (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen, smiLPUINT32 value) { smiINT32 length; smiINT32 i; if (ParseType (tmpPtr, tmpLen) == -1) return (FALSE); length = ParseLength(tmpPtr, tmpLen); if (length <= 0) return (FALSE); if ((smiUINT32)length > *tmpLen) return (FALSE); if ((length > 5) || ((length > 4) && (*(*tmpPtr) != 0x00))) return (FALSE); while (length && *(*tmpPtr) == 0x00) { // leading null octet? (*tmpPtr)++; // if so, skip it length--; // and don't count it (*tmpLen)--; // Adjust remaining msg length } *value = 0; for (i = 0; i < length; i++) *value = (*value << 8) + (smiUINT32)*(*tmpPtr)++; *tmpLen -= length; return (TRUE); } // end_ParseUInt() BOOL ParseInt (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen, smiLPINT value) { smiINT32 length; smiINT32 i; smiINT32 sign; if (ParseType (tmpPtr, tmpLen) != SNMP_SYNTAX_INT) return (FALSE); length = ParseLength (tmpPtr, tmpLen); if (length <= 0) return (FALSE); if ((smiUINT32)length > *tmpLen || length > 4) return (FALSE); sign = ((*(*tmpPtr) & 0x80) == 0x00) ? 0x00 : 0xFF; *value = 0; for (i = 0; i < length; i++) *value = (*value << 8) + (smiUINT32) *(*tmpPtr)++; // sign-extend upper bits for (i = length; i < 4; i++) *value = *value + (sign << i * 8); *tmpLen -= length; return (TRUE); } // end_ParseInt() BOOL ParseNull (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen) { smiINT32 length; if (ParseType (tmpPtr, tmpLen) == -1) return (FALSE); length = ParseLength (tmpPtr, tmpLen); if (length != 0) // NULLs have no length return (FALSE); return (TRUE); } // end_ParseNull BOOL ParseSequence (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen) { if (ParseType (tmpPtr, tmpLen) != SNMP_SYNTAX_SEQUENCE) return (FALSE); if (ParseLength (tmpPtr, tmpLen) == -1) return (FALSE); return (TRUE); } // end_ParseSequence smiINT32 ParseType (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen) { // 980421 - BobN // - replaced tmpLen logic with working_len logic // - working_len is always checked on entry into a // - Parse function smiINT32 type; if (*tmpLen == 0) return (-1); type = *(*tmpPtr)++; (*tmpLen)--; // Adjust remaining msg length switch (type) { case SNMP_SYNTAX_INT: case SNMP_SYNTAX_OCTETS: case SNMP_SYNTAX_OID: case SNMP_SYNTAX_SEQUENCE: case SNMP_SYNTAX_IPADDR: case SNMP_SYNTAX_CNTR32: case SNMP_SYNTAX_GAUGE32: case SNMP_SYNTAX_TIMETICKS: case SNMP_SYNTAX_OPAQUE: case SNMP_SYNTAX_UINT32: case SNMP_SYNTAX_CNTR64: case SNMP_SYNTAX_NULL: case SNMP_SYNTAX_NOSUCHOBJECT: case SNMP_SYNTAX_NOSUCHINSTANCE: case SNMP_SYNTAX_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: type = -1; break; } return (type); } // end_ParseType smiINT32 ParseLength (smiLPBYTE *tmpPtr, smiLPUINT32 tmpLen) { // 980421 - BobN // - replaced end_ptr logic with tmpLen logic // - tmpLen is always checked on entry into a Parse // - function and is decremented as used therein. smiINT32 length; smiINT32 lenlen; if (*tmpLen == 0) return (-1); length = (smiINT32) *(*tmpPtr)++; (*tmpLen)--; // Adjust remaining msg length // Check for short-form value if (length < 0x80) return (length); // Long form lenlen = length & 0x7F; if ((smiUINT32)lenlen > *tmpLen || lenlen > 4 || lenlen < 1) return (-1); // Out of bounds *tmpLen -= lenlen; // Adjust remaining msg length length = 0; while (lenlen) { length = (length << 8) + *(*tmpPtr)++; lenlen--; } return (length); } // end_ParseLength