aboutsummaryrefslogtreecommitdiff
path: root/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.c
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.c')
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.c749
1 files changed, 749 insertions, 0 deletions
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.c
new file mode 100644
index 0000000..f35b760
--- /dev/null
+++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/external/lwip/src/apps/snmp/snmp_asn1.c
@@ -0,0 +1,749 @@
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) encoding
+ *
+ * @todo not optimised (yet), favor correctness over speed, favor speed over size
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ * Martin Hentschel <info@cl-soft.de>
+ */
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "snmp_asn1.h"
+
+#define PBUF_OP_EXEC(code) \
+ if ((code) != ERR_OK) { \
+ return ERR_BUF; \
+ }
+
+/**
+ * Encodes a TLV into a pbuf stream.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param tlv TLV to encode
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
+{
+ u8_t data;
+ u8_t length_bytes_required;
+
+ /* write type */
+ if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
+ /* extended format is not used by SNMP so we do not accept those values */
+ return ERR_ARG;
+ }
+ if (tlv->type_len != 0) {
+ /* any other value as auto is not accepted for type (we always use one byte because extended syntax is prohibited) */
+ return ERR_ARG;
+ }
+
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, tlv->type));
+ tlv->type_len = 1;
+
+ /* write length */
+ if (tlv->value_len <= 127) {
+ length_bytes_required = 1;
+ } else if (tlv->value_len <= 255) {
+ length_bytes_required = 2;
+ } else {
+ length_bytes_required = 3;
+ }
+
+ /* check for forced min length */
+ if (tlv->length_len > 0) {
+ if (tlv->length_len < length_bytes_required) {
+ /* unable to code requested length in requested number of bytes */
+ return ERR_ARG;
+ }
+
+ length_bytes_required = tlv->length_len;
+ } else {
+ tlv->length_len = length_bytes_required;
+ }
+
+ if (length_bytes_required > 1) {
+ /* multi byte representation required */
+ length_bytes_required--;
+ data = 0x80 | length_bytes_required; /* extended length definition, 1 length byte follows */
+
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
+
+ while (length_bytes_required > 1) {
+ if (length_bytes_required == 2) {
+ /* append high byte */
+ data = (u8_t)(tlv->value_len >> 8);
+ } else {
+ /* append leading 0x00 */
+ data = 0x00;
+ }
+
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
+ length_bytes_required--;
+ }
+ }
+
+ /* append low byte */
+ data = (u8_t)(tlv->value_len & 0xFF);
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
+
+ return ERR_OK;
+}
+
+/**
+ * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param raw_len raw data length
+ * @param raw points raw data
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len)
+{
+ PBUF_OP_EXEC(snmp_pbuf_stream_writebuf(pbuf_stream, raw, raw_len));
+
+ return ERR_OK;
+}
+
+/**
+ * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
+ * @param value is the host order u32_t value to be encoded
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_u32t_cnt()
+ */
+err_t
+snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value)
+{
+ if (octets_needed > 5) {
+ return ERR_ARG;
+ }
+ if (octets_needed == 5) {
+ /* not enough bits in 'value' add leading 0x00 */
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
+ octets_needed--;
+ }
+
+ while (octets_needed > 1) {
+ octets_needed--;
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
+ }
+
+ /* (only) one least significant octet */
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
+
+ return ERR_OK;
+}
+
+/**
+ * Encodes u64_t (counter64) into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
+ * @param value is the host order u32_t value to be encoded
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_u64t_cnt()
+ */
+err_t
+snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value)
+{
+ if (octets_needed > 9) {
+ return ERR_ARG;
+ }
+ if (octets_needed == 9) {
+ /* not enough bits in 'value' add leading 0x00 */
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
+ octets_needed--;
+ }
+
+ while (octets_needed > 4) {
+ octets_needed--;
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> ((octets_needed-4) << 3))));
+ }
+
+ /* skip to low u32 */
+ value++;
+
+ while (octets_needed > 1) {
+ octets_needed--;
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> (octets_needed << 3))));
+ }
+
+ /* always write at least one octet (also in case of value == 0) */
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value)));
+
+ return ERR_OK;
+}
+
+/**
+ * Encodes s32_t integer into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
+ * @param value is the host order s32_t value to be encoded
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_s32t_cnt()
+ */
+err_t
+snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value)
+{
+ while (octets_needed > 1) {
+ octets_needed--;
+
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
+ }
+
+ /* (only) one least significant octet */
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
+
+ return ERR_OK;
+}
+
+/**
+ * Encodes object identifier into a pbuf chained ASN1 msg.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param oid points to object identifier array
+ * @param oid_len object identifier array length
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len)
+{
+ if (oid_len > 1) {
+ /* write compressed first two sub id's */
+ u32_t compressed_byte = ((oid[0] * 40) + oid[1]);
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)compressed_byte));
+ oid_len -= 2;
+ oid += 2;
+ } else {
+ /* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
+ /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
+ return ERR_ARG;
+ }
+
+ while (oid_len > 0) {
+ u32_t sub_id;
+ u8_t shift, tail;
+
+ oid_len--;
+ sub_id = *oid;
+ tail = 0;
+ shift = 28;
+ while (shift > 0) {
+ u8_t code;
+
+ code = (u8_t)(sub_id >> shift);
+ if ((code != 0) || (tail != 0)) {
+ tail = 1;
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80));
+ }
+ shift -= 7;
+ }
+ PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F));
+
+ /* proceed to next sub-identifier */
+ oid++;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Returns octet count for length.
+ *
+ * @param length parameter length
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
+{
+ if (length < 0x80U) {
+ *octets_needed = 1;
+ } else if (length < 0x100U) {
+ *octets_needed = 2;
+ } else {
+ *octets_needed = 3;
+ }
+}
+
+/**
+ * Returns octet count for an u32_t.
+ *
+ * @param value value to be encoded
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+void
+snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
+{
+ if (value < 0x80UL) {
+ *octets_needed = 1;
+ } else if (value < 0x8000UL) {
+ *octets_needed = 2;
+ } else if (value < 0x800000UL) {
+ *octets_needed = 3;
+ } else if (value < 0x80000000UL) {
+ *octets_needed = 4;
+ } else {
+ *octets_needed = 5;
+ }
+}
+
+/**
+ * Returns octet count for an u64_t.
+ *
+ * @param value value to be encoded
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+void
+snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed)
+{
+ /* check if high u32 is 0 */
+ if (*value == 0x00) {
+ /* only low u32 is important */
+ value++;
+ snmp_asn1_enc_u32t_cnt(*value, octets_needed);
+ } else {
+ /* low u32 does not matter for length determination */
+ snmp_asn1_enc_u32t_cnt(*value, octets_needed);
+ *octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */
+ }
+}
+
+/**
+ * Returns octet count for an s32_t.
+ *
+ * @param value value to be encoded
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed.
+ */
+void
+snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
+{
+ if (value < 0) {
+ value = ~value;
+ }
+ if (value < 0x80L) {
+ *octets_needed = 1;
+ } else if (value < 0x8000L) {
+ *octets_needed = 2;
+ } else if (value < 0x800000L) {
+ *octets_needed = 3;
+ } else {
+ *octets_needed = 4;
+ }
+}
+
+/**
+ * Returns octet count for an object identifier.
+ *
+ * @param oid points to object identifier array
+ * @param oid_len object identifier array length
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed)
+{
+ u32_t sub_id;
+
+ *octets_needed = 0;
+ if (oid_len > 1) {
+ /* compressed prefix in one octet */
+ (*octets_needed)++;
+ oid_len -= 2;
+ oid += 2;
+ }
+ while (oid_len > 0) {
+ oid_len--;
+ sub_id = *oid;
+
+ sub_id >>= 7;
+ (*octets_needed)++;
+ while (sub_id > 0) {
+ sub_id >>= 7;
+ (*octets_needed)++;
+ }
+ oid++;
+ }
+}
+
+/**
+ * Decodes a TLV from a pbuf stream.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param tlv returns decoded TLV
+ * @return ERR_OK if successful, ERR_VAL if we can't decode
+ */
+err_t
+snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
+{
+ u8_t data;
+
+ /* decode type first */
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ tlv->type = data;
+
+ if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
+ /* extended format is not used by SNMP so we do not accept those values */
+ return ERR_VAL;
+ }
+ tlv->type_len = 1;
+
+ /* now, decode length */
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+ if (data < 0x80) { /* short form */
+ tlv->length_len = 1;
+ tlv->value_len = data;
+ } else if (data > 0x80) { /* long form */
+ u8_t length_bytes = data - 0x80;
+ tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */
+ tlv->value_len = 0;
+
+ while (length_bytes > 0) {
+ /* we only support up to u16.maxvalue-1 (2 bytes) but have to accept leading zero bytes */
+ if (tlv->value_len > 0xFF) {
+ return ERR_VAL;
+ }
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ tlv->value_len <<= 8;
+ tlv->value_len |= data;
+
+ /* take care for special value used for indefinite length */
+ if (tlv->value_len == 0xFFFF) {
+ return ERR_VAL;
+ }
+
+ length_bytes--;
+ }
+ } else { /* data == 0x80 indefinite length form */
+ /* (not allowed for SNMP; RFC 1157, 3.2.2) */
+ return ERR_VAL;
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Decodes positive integer (counter, gauge, timeticks) into u32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+err_t
+snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
+{
+ u8_t data;
+
+ if ((len > 0) && (len <= 5)) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+ /* expecting sign bit to be zero, only unsigned please! */
+ if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) {
+ *value = data;
+ len--;
+
+ while (len > 0) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ len--;
+
+ *value <<= 8;
+ *value |= data;
+ }
+
+ return ERR_OK;
+ }
+ }
+
+ return ERR_VAL;
+}
+
+/**
+ * Decodes large positive integer (counter64) into 2x u32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+err_t
+snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
+{
+ u8_t data;
+
+ if (len <= 4) {
+ /* high u32 is 0 */
+ *value = 0;
+ /* directly skip to low u32 */
+ value++;
+ }
+
+ if ((len > 0) && (len <= 9)) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+ /* expecting sign bit to be zero, only unsigned please! */
+ if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) {
+ *value = data;
+ len--;
+
+ while (len > 0) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+ if (len == 4) {
+ /* skip to low u32 */
+ value++;
+ *value = 0;
+ } else {
+ *value <<= 8;
+ }
+
+ *value |= data;
+ len--;
+ }
+
+ return ERR_OK;
+ }
+ }
+
+ return ERR_VAL;
+}
+
+/**
+ * Decodes integer into s32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed!
+ */
+err_t
+snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ u8_t *lsb_ptr = (u8_t*)value;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
+#endif
+ u8_t sign;
+ u8_t data;
+
+ if ((len > 0) && (len < 5)) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ len--;
+
+ if (data & 0x80) {
+ /* negative, start from -1 */
+ *value = -1;
+ sign = 1;
+ *lsb_ptr &= data;
+ } else {
+ /* positive, start from 0 */
+ *value = 0;
+ sign = 0;
+ *lsb_ptr |= data;
+ }
+
+ /* OR/AND octets with value */
+ while (len > 0) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ len--;
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ *value <<= 8;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ *value >>= 8;
+#endif
+
+ if (sign) {
+ *lsb_ptr |= 255;
+ *lsb_ptr &= data;
+ } else {
+ *lsb_ptr |= data;
+ }
+ }
+
+ return ERR_OK;
+ }
+
+ return ERR_VAL;
+}
+
+/**
+ * Decodes object identifier from incoming message into array of u32_t.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded object identifier
+ * @param oid return decoded object identifier
+ * @param oid_len return decoded object identifier length
+ * @param oid_max_len size of oid buffer
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t* oid, u8_t* oid_len, u8_t oid_max_len)
+{
+ u32_t *oid_ptr;
+ u8_t data;
+
+ *oid_len = 0;
+ oid_ptr = oid;
+ if (len > 0) {
+ if (oid_max_len < 2) {
+ return ERR_MEM;
+ }
+
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ len--;
+
+ /* first compressed octet */
+ if (data == 0x2B) {
+ /* (most) common case 1.3 (iso.org) */
+ *oid_ptr = 1;
+ oid_ptr++;
+ *oid_ptr = 3;
+ oid_ptr++;
+ } else if (data < 40) {
+ *oid_ptr = 0;
+ oid_ptr++;
+ *oid_ptr = data;
+ oid_ptr++;
+ } else if (data < 80) {
+ *oid_ptr = 1;
+ oid_ptr++;
+ *oid_ptr = data - 40;
+ oid_ptr++;
+ } else {
+ *oid_ptr = 2;
+ oid_ptr++;
+ *oid_ptr = data - 80;
+ oid_ptr++;
+ }
+ *oid_len = 2;
+ } else {
+ /* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */
+ return ERR_OK;
+ }
+
+ while ((len > 0) && (*oid_len < oid_max_len)) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ len--;
+
+ if ((data & 0x80) == 0x00) {
+ /* sub-identifier uses single octet */
+ *oid_ptr = data;
+ } else {
+ /* sub-identifier uses multiple octets */
+ u32_t sub_id = (data & ~0x80);
+ while ((len > 0) && ((data & 0x80) != 0)) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ len--;
+
+ sub_id = (sub_id << 7) + (data & ~0x80);
+ }
+
+ if ((data & 0x80) != 0) {
+ /* "more bytes following" bit still set at end of len */
+ return ERR_VAL;
+ }
+ *oid_ptr = sub_id;
+ }
+ oid_ptr++;
+ (*oid_len)++;
+ }
+
+ if (len > 0) {
+ /* OID to long to fit in our buffer */
+ return ERR_MEM;
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
+ * from incoming message into array.
+ *
+ * @param pbuf_stream points to a pbuf stream
+ * @param len length of the coded raw data (zero is valid, e.g. empty string!)
+ * @param buf return raw bytes
+ * @param buf_len returns length of the raw return value
+ * @param buf_max_len buffer size
+ * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t* buf_len, u16_t buf_max_len)
+{
+ if (len > buf_max_len) {
+ /* not enough dst space */
+ return ERR_MEM;
+ }
+ *buf_len = len;
+
+ while (len > 0) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf));
+ buf++;
+ len--;
+ }
+
+ return ERR_OK;
+}
+
+#endif /* LWIP_SNMP */