3 * Abstract Syntax Notation One (ISO 8824, 8825) encoding
5 * @todo not optimised (yet), favor correctness over speed, favor speed over size
9 * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
10 * All rights reserved.
12 * Redistribution and use in source and binary forms, with or without modification,
13 * are permitted provided that the following conditions are met:
15 * 1. Redistributions of source code must retain the above copyright notice,
16 * this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright notice,
18 * this list of conditions and the following disclaimer in the documentation
19 * and/or other materials provided with the distribution.
20 * 3. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
24 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
26 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
28 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
31 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
34 * Author: Christiaan Simons <christiaan.simons@axon.tv>
35 * Martin Hentschel <info@cl-soft.de>
38 #include "lwip/apps/snmp_opts.h"
40 #if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
42 #include "snmp_asn1.h"
44 #define PBUF_OP_EXEC(code) \
45 if ((code) != ERR_OK) { \
50 * Encodes a TLV into a pbuf stream.
52 * @param pbuf_stream points to a pbuf stream
53 * @param tlv TLV to encode
54 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
57 snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
60 u8_t length_bytes_required;
63 if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
64 /* extended format is not used by SNMP so we do not accept those values */
67 if (tlv->type_len != 0) {
68 /* any other value as auto is not accepted for type (we always use one byte because extended syntax is prohibited) */
72 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, tlv->type));
76 if (tlv->value_len <= 127) {
77 length_bytes_required = 1;
78 } else if (tlv->value_len <= 255) {
79 length_bytes_required = 2;
81 length_bytes_required = 3;
84 /* check for forced min length */
85 if (tlv->length_len > 0) {
86 if (tlv->length_len < length_bytes_required) {
87 /* unable to code requested length in requested number of bytes */
91 length_bytes_required = tlv->length_len;
93 tlv->length_len = length_bytes_required;
96 if (length_bytes_required > 1) {
97 /* multi byte representation required */
98 length_bytes_required--;
99 data = 0x80 | length_bytes_required; /* extended length definition, 1 length byte follows */
101 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
103 while (length_bytes_required > 1) {
104 if (length_bytes_required == 2) {
105 /* append high byte */
106 data = (u8_t)(tlv->value_len >> 8);
108 /* append leading 0x00 */
112 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
113 length_bytes_required--;
117 /* append low byte */
118 data = (u8_t)(tlv->value_len & 0xFF);
119 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
125 * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
127 * @param pbuf_stream points to a pbuf stream
128 * @param raw_len raw data length
129 * @param raw points raw data
130 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
133 snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len)
135 PBUF_OP_EXEC(snmp_pbuf_stream_writebuf(pbuf_stream, raw, raw_len));
141 * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
143 * @param pbuf_stream points to a pbuf stream
144 * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
145 * @param value is the host order u32_t value to be encoded
146 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
148 * @see snmp_asn1_enc_u32t_cnt()
151 snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value)
153 if (octets_needed > 5) {
156 if (octets_needed == 5) {
157 /* not enough bits in 'value' add leading 0x00 */
158 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
162 while (octets_needed > 1) {
164 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
167 /* (only) one least significant octet */
168 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
174 * Encodes u64_t (counter64) into a pbuf chained ASN1 msg.
176 * @param pbuf_stream points to a pbuf stream
177 * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
178 * @param value is the host order u32_t value to be encoded
179 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
181 * @see snmp_asn1_enc_u64t_cnt()
184 snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value)
186 if (octets_needed > 9) {
189 if (octets_needed == 9) {
190 /* not enough bits in 'value' add leading 0x00 */
191 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
195 while (octets_needed > 4) {
197 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> ((octets_needed-4) << 3))));
200 /* skip to low u32 */
203 while (octets_needed > 1) {
205 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> (octets_needed << 3))));
208 /* always write at least one octet (also in case of value == 0) */
209 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value)));
215 * Encodes s32_t integer into a pbuf chained ASN1 msg.
217 * @param pbuf_stream points to a pbuf stream
218 * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
219 * @param value is the host order s32_t value to be encoded
220 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
222 * @see snmp_asn1_enc_s32t_cnt()
225 snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value)
227 while (octets_needed > 1) {
230 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
233 /* (only) one least significant octet */
234 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
240 * Encodes object identifier into a pbuf chained ASN1 msg.
242 * @param pbuf_stream points to a pbuf stream
243 * @param oid points to object identifier array
244 * @param oid_len object identifier array length
245 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
248 snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len)
251 /* write compressed first two sub id's */
252 u32_t compressed_byte = ((oid[0] * 40) + oid[1]);
253 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)compressed_byte));
257 /* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
258 /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
262 while (oid_len > 0) {
273 code = (u8_t)(sub_id >> shift);
274 if ((code != 0) || (tail != 0)) {
276 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80));
280 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F));
282 /* proceed to next sub-identifier */
289 * Returns octet count for length.
291 * @param length parameter length
292 * @param octets_needed points to the return value
295 snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
297 if (length < 0x80U) {
299 } else if (length < 0x100U) {
307 * Returns octet count for an u32_t.
309 * @param value value to be encoded
310 * @param octets_needed points to the return value
312 * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
313 * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
314 * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
317 snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
319 if (value < 0x80UL) {
321 } else if (value < 0x8000UL) {
323 } else if (value < 0x800000UL) {
325 } else if (value < 0x80000000UL) {
333 * Returns octet count for an u64_t.
335 * @param value value to be encoded
336 * @param octets_needed points to the return value
338 * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
339 * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
340 * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
343 snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed)
345 /* check if high u32 is 0 */
346 if (*value == 0x00) {
347 /* only low u32 is important */
349 snmp_asn1_enc_u32t_cnt(*value, octets_needed);
351 /* low u32 does not matter for length determination */
352 snmp_asn1_enc_u32t_cnt(*value, octets_needed);
353 *octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */
358 * Returns octet count for an s32_t.
360 * @param value value to be encoded
361 * @param octets_needed points to the return value
363 * @note ASN coded integers are _always_ signed.
366 snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
373 } else if (value < 0x8000L) {
375 } else if (value < 0x800000L) {
383 * Returns octet count for an object identifier.
385 * @param oid points to object identifier array
386 * @param oid_len object identifier array length
387 * @param octets_needed points to the return value
390 snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed)
396 /* compressed prefix in one octet */
401 while (oid_len > 0) {
416 * Decodes a TLV from a pbuf stream.
418 * @param pbuf_stream points to a pbuf stream
419 * @param tlv returns decoded TLV
420 * @return ERR_OK if successful, ERR_VAL if we can't decode
423 snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
427 /* decode type first */
428 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
431 if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
432 /* extended format is not used by SNMP so we do not accept those values */
437 /* now, decode length */
438 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
440 if (data < 0x80) { /* short form */
442 tlv->value_len = data;
443 } else if (data > 0x80) { /* long form */
444 u8_t length_bytes = data - 0x80;
445 tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */
448 while (length_bytes > 0) {
449 /* we only support up to u16.maxvalue-1 (2 bytes) but have to accept leading zero bytes */
450 if (tlv->value_len > 0xFF) {
453 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
454 tlv->value_len <<= 8;
455 tlv->value_len |= data;
457 /* take care for special value used for indefinite length */
458 if (tlv->value_len == 0xFFFF) {
464 } else { /* data == 0x80 indefinite length form */
465 /* (not allowed for SNMP; RFC 1157, 3.2.2) */
473 * Decodes positive integer (counter, gauge, timeticks) into u32_t.
475 * @param pbuf_stream points to a pbuf stream
476 * @param len length of the coded integer field
477 * @param value return host order integer
478 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
480 * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
481 * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
482 * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
485 snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
489 if ((len > 0) && (len <= 5)) {
490 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
492 /* expecting sign bit to be zero, only unsigned please! */
493 if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) {
498 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
513 * Decodes large positive integer (counter64) into 2x u32_t.
515 * @param pbuf_stream points to a pbuf stream
516 * @param len length of the coded integer field
517 * @param value return host order integer
518 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
520 * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
521 * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
522 * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
525 snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
532 /* directly skip to low u32 */
536 if ((len > 0) && (len <= 9)) {
537 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
539 /* expecting sign bit to be zero, only unsigned please! */
540 if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) {
545 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
548 /* skip to low u32 */
567 * Decodes integer into s32_t.
569 * @param pbuf_stream points to a pbuf stream
570 * @param len length of the coded integer field
571 * @param value return host order integer
572 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
574 * @note ASN coded integers are _always_ signed!
577 snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value)
579 #if BYTE_ORDER == LITTLE_ENDIAN
580 u8_t *lsb_ptr = (u8_t*)value;
582 #if BYTE_ORDER == BIG_ENDIAN
583 u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
588 if ((len > 0) && (len < 5)) {
589 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
593 /* negative, start from -1 */
598 /* positive, start from 0 */
604 /* OR/AND octets with value */
606 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
609 #if BYTE_ORDER == LITTLE_ENDIAN
612 #if BYTE_ORDER == BIG_ENDIAN
631 * Decodes object identifier from incoming message into array of u32_t.
633 * @param pbuf_stream points to a pbuf stream
634 * @param len length of the coded object identifier
635 * @param oid return decoded object identifier
636 * @param oid_len return decoded object identifier length
637 * @param oid_max_len size of oid buffer
638 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
641 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)
649 if (oid_max_len < 2) {
653 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
656 /* first compressed octet */
658 /* (most) common case 1.3 (iso.org) */
663 } else if (data < 40) {
668 } else if (data < 80) {
671 *oid_ptr = data - 40;
676 *oid_ptr = data - 80;
681 /* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */
685 while ((len > 0) && (*oid_len < oid_max_len)) {
686 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
689 if ((data & 0x80) == 0x00) {
690 /* sub-identifier uses single octet */
693 /* sub-identifier uses multiple octets */
694 u32_t sub_id = (data & ~0x80);
695 while ((len > 0) && ((data & 0x80) != 0)) {
696 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
699 sub_id = (sub_id << 7) + (data & ~0x80);
702 if ((data & 0x80) != 0) {
703 /* "more bytes following" bit still set at end of len */
713 /* OID to long to fit in our buffer */
721 * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
722 * from incoming message into array.
724 * @param pbuf_stream points to a pbuf stream
725 * @param len length of the coded raw data (zero is valid, e.g. empty string!)
726 * @param buf return raw bytes
727 * @param buf_len returns length of the raw return value
728 * @param buf_max_len buffer size
729 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
732 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)
734 if (len > buf_max_len) {
735 /* not enough dst space */
741 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf));
749 #endif /* LWIP_SNMP */