3 * Abstract Syntax Notation One (ISO 8824, 8825) decoding
\r
5 * @todo not optimised (yet), favor correctness over speed, favor speed over size
\r
9 * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
\r
10 * All rights reserved.
\r
12 * Redistribution and use in source and binary forms, with or without modification,
\r
13 * are permitted provided that the following conditions are met:
\r
15 * 1. Redistributions of source code must retain the above copyright notice,
\r
16 * this list of conditions and the following disclaimer.
\r
17 * 2. Redistributions in binary form must reproduce the above copyright notice,
\r
18 * this list of conditions and the following disclaimer in the documentation
\r
19 * and/or other materials provided with the distribution.
\r
20 * 3. The name of the author may not be used to endorse or promote products
\r
21 * derived from this software without specific prior written permission.
\r
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
\r
24 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
\r
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
\r
26 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
\r
27 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
\r
28 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
\r
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
\r
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
\r
31 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
\r
34 * Author: Christiaan Simons <christiaan.simons@axon.tv>
\r
37 #include "lwip/opt.h"
\r
39 #if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
\r
41 #include "lwip/snmp_asn1.h"
\r
44 * Retrieves type field from incoming pbuf chain.
\r
46 * @param p points to a pbuf holding an ASN1 coded type field
\r
47 * @param ofs points to the offset within the pbuf chain of the ASN1 coded type field
\r
48 * @param type return ASN1 type
\r
49 * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
\r
52 snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type)
\r
64 msg_ptr = p->payload;
\r
65 msg_ptr += ofs - base;
\r
71 /* p == NULL, ofs >= plen */
\r
76 * Decodes length field from incoming pbuf chain into host length.
\r
78 * @param p points to a pbuf holding an ASN1 coded length
\r
79 * @param ofs points to the offset within the pbuf chain of the ASN1 coded length
\r
80 * @param octets_used returns number of octets used by the length code
\r
81 * @param length return host order length, upto 64k
\r
82 * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
\r
85 snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length)
\r
97 msg_ptr = p->payload;
\r
98 msg_ptr += ofs - base;
\r
100 if (*msg_ptr < 0x80)
\r
102 /* primitive definite length format */
\r
104 *length = *msg_ptr;
\r
107 else if (*msg_ptr == 0x80)
\r
109 /* constructed indefinite length format, termination with two zero octets */
\r
125 /* next octet in next pbuf */
\r
127 if (p == NULL) { return ERR_ARG; }
\r
128 msg_ptr = p->payload;
\r
133 /* next octet in same pbuf */
\r
141 /* stop while (i > 0) */
\r
154 else if (*msg_ptr == 0x81)
\r
156 /* constructed definite length format, one octet */
\r
160 /* next octet in next pbuf */
\r
162 if (p == NULL) { return ERR_ARG; }
\r
163 msg_ptr = p->payload;
\r
167 /* next octet in same pbuf */
\r
170 *length = *msg_ptr;
\r
174 else if (*msg_ptr == 0x82)
\r
178 /* constructed definite length format, two octets */
\r
186 /* next octet in next pbuf */
\r
188 if (p == NULL) { return ERR_ARG; }
\r
189 msg_ptr = p->payload;
\r
194 /* next octet in same pbuf */
\r
199 /* least significant length octet */
\r
200 *length |= *msg_ptr;
\r
204 /* most significant length octet */
\r
205 *length = (*msg_ptr) << 8;
\r
213 /* constructed definite length format 3..127 octets, this is too big (>64k) */
\r
214 /** @todo: do we need to accept inefficient codings with many leading zero's? */
\r
215 *octets_used = 1 + ((*msg_ptr) & 0x7f);
\r
222 /* p == NULL, ofs >= plen */
\r
227 * Decodes positive integer (counter, gauge, timeticks) into u32_t.
\r
229 * @param p points to a pbuf holding an ASN1 coded integer
\r
230 * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
\r
231 * @param len length of the coded integer field
\r
232 * @param value return host order integer
\r
233 * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
\r
235 * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
\r
236 * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
\r
237 * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
\r
240 snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value)
\r
252 msg_ptr = p->payload;
\r
253 msg_ptr += ofs - base;
\r
254 if ((len > 0) && (len < 6))
\r
256 /* start from zero */
\r
258 if (*msg_ptr & 0x80)
\r
260 /* negative, expecting zero sign bit! */
\r
266 if ((len > 1) && (*msg_ptr == 0))
\r
268 /* skip leading "sign byte" octet 0x00 */
\r
273 /* next octet in next pbuf */
\r
275 if (p == NULL) { return ERR_ARG; }
\r
276 msg_ptr = p->payload;
\r
281 /* next octet in same pbuf */
\r
286 /* OR octets with value */
\r
290 *value |= *msg_ptr;
\r
295 /* next octet in next pbuf */
\r
297 if (p == NULL) { return ERR_ARG; }
\r
298 msg_ptr = p->payload;
\r
303 /* next octet in same pbuf */
\r
307 *value |= *msg_ptr;
\r
317 /* p == NULL, ofs >= plen */
\r
322 * Decodes integer into s32_t.
\r
324 * @param p points to a pbuf holding an ASN1 coded integer
\r
325 * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
\r
326 * @param len length of the coded integer field
\r
327 * @param value return host order integer
\r
328 * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
\r
330 * @note ASN coded integers are _always_ signed!
\r
333 snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value)
\r
337 #if BYTE_ORDER == LITTLE_ENDIAN
\r
338 u8_t *lsb_ptr = (u8_t*)value;
\r
340 #if BYTE_ORDER == BIG_ENDIAN
\r
341 u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
\r
352 msg_ptr = p->payload;
\r
353 msg_ptr += ofs - base;
\r
354 if ((len > 0) && (len < 5))
\r
356 if (*msg_ptr & 0x80)
\r
358 /* negative, start from -1 */
\r
364 /* positive, start from 0 */
\r
368 /* OR/AND octets with value */
\r
374 *lsb_ptr &= *msg_ptr;
\r
380 *lsb_ptr |= *msg_ptr;
\r
386 /* next octet in next pbuf */
\r
388 if (p == NULL) { return ERR_ARG; }
\r
389 msg_ptr = p->payload;
\r
394 /* next octet in same pbuf */
\r
400 *lsb_ptr &= *msg_ptr;
\r
404 *lsb_ptr |= *msg_ptr;
\r
415 /* p == NULL, ofs >= plen */
\r
420 * Decodes object identifier from incoming message into array of s32_t.
\r
422 * @param p points to a pbuf holding an ASN1 coded object identifier
\r
423 * @param ofs points to the offset within the pbuf chain of the ASN1 coded object identifier
\r
424 * @param len length of the coded object identifier
\r
425 * @param oid return object identifier struct
\r
426 * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
\r
429 snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid)
\r
442 msg_ptr = p->payload;
\r
443 msg_ptr += ofs - base;
\r
446 oid_ptr = &oid->id[0];
\r
449 /* first compressed octet */
\r
450 if (*msg_ptr == 0x2B)
\r
452 /* (most) common case 1.3 (iso.org) */
\r
458 else if (*msg_ptr < 40)
\r
462 *oid_ptr = *msg_ptr;
\r
465 else if (*msg_ptr < 80)
\r
469 *oid_ptr = (*msg_ptr) - 40;
\r
476 *oid_ptr = (*msg_ptr) - 80;
\r
483 /* accepting zero length identifiers e.g. for
\r
484 getnext operation. uncommon but valid */
\r
493 /* next octet in next pbuf */
\r
495 if (p == NULL) { return ERR_ARG; }
\r
496 msg_ptr = p->payload;
\r
501 /* next octet in same pbuf */
\r
505 while ((len > 0) && (oid->len < LWIP_SNMP_OBJ_ID_LEN))
\r
507 /* sub-identifier uses multiple octets */
\r
508 if (*msg_ptr & 0x80)
\r
512 while ((*msg_ptr & 0x80) && (len > 1))
\r
515 sub_id = (sub_id << 7) + (*msg_ptr & ~0x80);
\r
519 /* next octet in next pbuf */
\r
521 if (p == NULL) { return ERR_ARG; }
\r
522 msg_ptr = p->payload;
\r
527 /* next octet in same pbuf */
\r
531 if (!(*msg_ptr & 0x80) && (len > 0))
\r
533 /* last octet sub-identifier */
\r
535 sub_id = (sub_id << 7) + *msg_ptr;
\r
541 /* !(*msg_ptr & 0x80) sub-identifier uses single octet */
\r
543 *oid_ptr = *msg_ptr;
\r
547 /* remaining oid bytes available ... */
\r
551 /* next octet in next pbuf */
\r
553 if (p == NULL) { return ERR_ARG; }
\r
554 msg_ptr = p->payload;
\r
559 /* next octet in same pbuf */
\r
568 /* len == 0, end of oid */
\r
573 /* len > 0, oid->len == LWIP_SNMP_OBJ_ID_LEN or malformed encoding */
\r
580 /* p == NULL, ofs >= plen */
\r
585 * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
\r
586 * from incoming message into array.
\r
588 * @param p points to a pbuf holding an ASN1 coded raw data
\r
589 * @param ofs points to the offset within the pbuf chain of the ASN1 coded raw data
\r
590 * @param len length of the coded raw data (zero is valid, e.g. empty string!)
\r
591 * @param raw_len length of the raw return value
\r
592 * @param raw return raw bytes
\r
593 * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
\r
596 snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw)
\r
610 msg_ptr = p->payload;
\r
611 msg_ptr += ofs - base;
\r
612 if (raw_len >= len)
\r
616 /* copy len - 1 octets */
\r
623 /* next octet in next pbuf */
\r
625 if (p == NULL) { return ERR_ARG; }
\r
626 msg_ptr = p->payload;
\r
631 /* next octet in same pbuf */
\r
635 /* copy last octet */
\r
641 /* raw_len < len, not enough dst space */
\r
647 /* p == NULL, ofs >= plen */
\r
652 /* len == 0, empty string */
\r
657 #endif /* LWIP_SNMP */
\r