1 /**
2  * @file
3  * Abstract Syntax Notation One (ISO 8824, 8825) encoding
4  *
5  * @todo not optimised (yet), favor correctness over speed, favor speed over size
6  */
7 
8 /*
9  * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without modification,
13  * are permitted provided that the following conditions are met:
14  *
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.
22  *
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
32  * OF SUCH DAMAGE.
33  *
34  * Author: Christiaan Simons <christiaan.simons@axon.tv>
35  *         Martin Hentschel <info@cl-soft.de>
36  */
37 
38 #include "lwip/apps/snmp_opts.h"
39 
40 #if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
41 
42 #include "snmp_asn1.h"
43 
44 #define PBUF_OP_EXEC(code) \
45   if ((code) != ERR_OK) { \
46     return ERR_BUF; \
47   }
48 
49 /**
50  * Encodes a TLV into a pbuf stream.
51  *
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
55  */
56 err_t
57 snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
58 {
59   u8_t data;
60   u8_t length_bytes_required;
61 
62   /* write type */
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 */
65     return ERR_ARG;
66   }
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) */
69     return ERR_ARG;
70   }
71 
72   PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, tlv->type));
73   tlv->type_len = 1;
74 
75   /* write length */
76   if (tlv->value_len <= 127) {
77     length_bytes_required = 1;
78   } else if (tlv->value_len <= 255) {
79     length_bytes_required = 2;
80   } else  {
81     length_bytes_required = 3;
82   }
83 
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 */
88       return ERR_ARG;
89     }
90 
91     length_bytes_required = tlv->length_len;
92   } else {
93     tlv->length_len = length_bytes_required;
94   }
95 
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 */
100 
101     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
102 
103     while (length_bytes_required > 1) {
104       if (length_bytes_required == 2) {
105         /* append high byte */
106         data = (u8_t)(tlv->value_len >> 8);
107       } else {
108         /* append leading 0x00 */
109         data = 0x00;
110       }
111 
112       PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
113       length_bytes_required--;
114     }
115   }
116 
117   /* append low byte */
118   data = (u8_t)(tlv->value_len & 0xFF);
119   PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
120 
121   return ERR_OK;
122 }
123 
124 /**
125  * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
126  *
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
131  */
132 err_t
133 snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len)
134 {
135   PBUF_OP_EXEC(snmp_pbuf_stream_writebuf(pbuf_stream, raw, raw_len));
136 
137   return ERR_OK;
138 }
139 
140 /**
141  * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
142  *
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
147  *
148  * @see snmp_asn1_enc_u32t_cnt()
149  */
150 err_t
151 snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value)
152 {
153   if (octets_needed > 5) {
154     return ERR_ARG;
155   }
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));
159     octets_needed--;
160   }
161 
162   while (octets_needed > 1) {
163     octets_needed--;
164     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
165   }
166 
167   /* (only) one least significant octet */
168   PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
169 
170   return ERR_OK;
171 }
172 
173 /**
174  * Encodes u64_t (counter64) into a pbuf chained ASN1 msg.
175  *
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
180  *
181  * @see snmp_asn1_enc_u64t_cnt()
182  */
183 err_t
184 snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value)
185 {
186   if (octets_needed > 9) {
187     return ERR_ARG;
188   }
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));
192     octets_needed--;
193   }
194 
195   while (octets_needed > 4) {
196     octets_needed--;
197     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> ((octets_needed-4) << 3))));
198   }
199 
200   /* skip to low u32 */
201   value++;
202 
203   while (octets_needed > 1) {
204     octets_needed--;
205     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> (octets_needed << 3))));
206   }
207 
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)));
210 
211   return ERR_OK;
212 }
213 
214 /**
215  * Encodes s32_t integer into a pbuf chained ASN1 msg.
216  *
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
221  *
222  * @see snmp_asn1_enc_s32t_cnt()
223  */
224 err_t
225 snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value)
226 {
227   while (octets_needed > 1) {
228     octets_needed--;
229 
230     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
231   }
232 
233   /* (only) one least significant octet */
234   PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
235 
236   return ERR_OK;
237 }
238 
239 /**
240  * Encodes object identifier into a pbuf chained ASN1 msg.
241  *
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
246  */
247 err_t
248 snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len)
249 {
250   if (oid_len > 1) {
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));
254     oid_len -= 2;
255     oid += 2;
256   } else {
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) */
259     return ERR_ARG;
260   }
261 
262   while (oid_len > 0) {
263     u32_t sub_id;
264     u8_t shift, tail;
265 
266     oid_len--;
267     sub_id = *oid;
268     tail = 0;
269     shift = 28;
270     while (shift > 0) {
271       u8_t code;
272 
273       code = (u8_t)(sub_id >> shift);
274       if ((code != 0) || (tail != 0)) {
275         tail = 1;
276         PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80));
277       }
278       shift -= 7;
279     }
280     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F));
281 
282     /* proceed to next sub-identifier */
283     oid++;
284   }
285   return ERR_OK;
286 }
287 
288 /**
289  * Returns octet count for length.
290  *
291  * @param length parameter length
292  * @param octets_needed points to the return value
293  */
294 void
295 snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
296 {
297   if (length < 0x80U) {
298     *octets_needed = 1;
299   } else if (length < 0x100U) {
300     *octets_needed = 2;
301   } else {
302     *octets_needed = 3;
303   }
304 }
305 
306 /**
307  * Returns octet count for an u32_t.
308  *
309  * @param value value to be encoded
310  * @param octets_needed points to the return value
311  *
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!!
315  */
316 void
317 snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
318 {
319   if (value < 0x80UL) {
320     *octets_needed = 1;
321   } else if (value < 0x8000UL) {
322     *octets_needed = 2;
323   } else if (value < 0x800000UL) {
324     *octets_needed = 3;
325   } else if (value < 0x80000000UL) {
326     *octets_needed = 4;
327   } else {
328     *octets_needed = 5;
329   }
330 }
331 
332 /**
333  * Returns octet count for an u64_t.
334  *
335  * @param value value to be encoded
336  * @param octets_needed points to the return value
337  *
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!!
341  */
342 void
343 snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed)
344 {
345   /* check if high u32 is 0 */
346   if (*value == 0x00) {
347     /* only low u32 is important */
348     value++;
349     snmp_asn1_enc_u32t_cnt(*value, octets_needed);
350   } else {
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 */
354   }
355 }
356 
357 /**
358  * Returns octet count for an s32_t.
359  *
360  * @param value value to be encoded
361  * @param octets_needed points to the return value
362  *
363  * @note ASN coded integers are _always_ signed.
364  */
365 void
366 snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
367 {
368   if (value < 0) {
369     value = ~value;
370   }
371   if (value < 0x80L) {
372     *octets_needed = 1;
373   } else if (value < 0x8000L) {
374     *octets_needed = 2;
375   } else if (value < 0x800000L) {
376     *octets_needed = 3;
377   } else {
378     *octets_needed = 4;
379   }
380 }
381 
382 /**
383  * Returns octet count for an object identifier.
384  *
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
388  */
389 void
390 snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed)
391 {
392   u32_t sub_id;
393 
394   *octets_needed = 0;
395   if (oid_len > 1) {
396     /* compressed prefix in one octet */
397     (*octets_needed)++;
398     oid_len -= 2;
399     oid += 2;
400   }
401   while (oid_len > 0) {
402     oid_len--;
403     sub_id = *oid;
404 
405     sub_id >>= 7;
406     (*octets_needed)++;
407     while (sub_id > 0) {
408       sub_id >>= 7;
409       (*octets_needed)++;
410     }
411     oid++;
412   }
413 }
414 
415 /**
416  * Decodes a TLV from a pbuf stream.
417  *
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
421  */
422 err_t
423 snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
424 {
425   u8_t data;
426 
427   /* decode type first */
428   PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
429   tlv->type = data;
430 
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 */
433     return ERR_VAL;
434   }
435   tlv->type_len = 1;
436 
437   /* now, decode length */
438   PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
439 
440   if (data < 0x80) { /* short form */
441     tlv->length_len = 1;
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 */
446     tlv->value_len = 0;
447 
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) {
451         return ERR_VAL;
452       }
453       PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
454       tlv->value_len <<= 8;
455       tlv->value_len |= data;
456 
457       /* take care for special value used for indefinite length */
458       if (tlv->value_len == 0xFFFF) {
459         return ERR_VAL;
460       }
461 
462       length_bytes--;
463     }
464   } else { /* data == 0x80 indefinite length form */
465     /* (not allowed for SNMP; RFC 1157, 3.2.2) */
466     return ERR_VAL;
467   }
468 
469   return ERR_OK;
470 }
471 
472 /**
473  * Decodes positive integer (counter, gauge, timeticks) into u32_t.
474  *
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
479  *
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!!
483  */
484 err_t
485 snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
486 {
487   u8_t data;
488 
489   if ((len > 0) && (len <= 5)) {
490     PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
491 
492     /* expecting sign bit to be zero, only unsigned please! */
493     if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) {
494       *value = data;
495       len--;
496 
497       while (len > 0) {
498         PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
499         len--;
500 
501         *value <<= 8;
502         *value |= data;
503       }
504 
505       return ERR_OK;
506     }
507   }
508 
509   return ERR_VAL;
510 }
511 
512 /**
513  * Decodes large positive integer (counter64) into 2x u32_t.
514  *
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
519  *
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!!
523  */
524 err_t
525 snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
526 {
527   u8_t data;
528 
529   if (len <= 4) {
530     /* high u32 is 0 */
531     *value = 0;
532     /* directly skip to low u32 */
533     value++;
534   }
535 
536   if ((len > 0) && (len <= 9)) {
537     PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
538 
539     /* expecting sign bit to be zero, only unsigned please! */
540     if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) {
541       *value = data;
542       len--;
543 
544       while (len > 0) {
545         PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
546 
547         if (len == 4) {
548           /* skip to low u32 */
549           value++;
550           *value = 0;
551         } else {
552           *value <<= 8;
553         }
554 
555         *value |= data;
556         len--;
557       }
558 
559       return ERR_OK;
560     }
561   }
562 
563   return ERR_VAL;
564 }
565 
566 /**
567  * Decodes integer into s32_t.
568  *
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
573  *
574  * @note ASN coded integers are _always_ signed!
575  */
576 err_t
577 snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value)
578 {
579 #if BYTE_ORDER == LITTLE_ENDIAN
580   u8_t *lsb_ptr = (u8_t*)value;
581 #endif
582 #if BYTE_ORDER == BIG_ENDIAN
583   u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
584 #endif
585   u8_t sign;
586   u8_t data;
587 
588   if ((len > 0) && (len < 5)) {
589     PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
590     len--;
591 
592     if (data & 0x80) {
593       /* negative, start from -1 */
594       *value = -1;
595       sign = 1;
596       *lsb_ptr &= data;
597     } else {
598       /* positive, start from 0 */
599       *value = 0;
600       sign = 0;
601       *lsb_ptr |= data;
602     }
603 
604     /* OR/AND octets with value */
605     while (len > 0) {
606       PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
607       len--;
608 
609 #if BYTE_ORDER == LITTLE_ENDIAN
610       *value <<= 8;
611 #endif
612 #if BYTE_ORDER == BIG_ENDIAN
613       *value >>= 8;
614 #endif
615 
616       if (sign) {
617         *lsb_ptr |= 255;
618         *lsb_ptr &= data;
619       } else {
620         *lsb_ptr |= data;
621       }
622     }
623 
624     return ERR_OK;
625   }
626 
627   return ERR_VAL;
628 }
629 
630 /**
631  * Decodes object identifier from incoming message into array of u32_t.
632  *
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
639  */
640 err_t
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)
642 {
643   u32_t *oid_ptr;
644   u8_t data;
645 
646   *oid_len = 0;
647   oid_ptr = oid;
648   if (len > 0) {
649     if (oid_max_len < 2) {
650       return ERR_MEM;
651     }
652 
653     PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
654     len--;
655 
656     /* first compressed octet */
657     if (data == 0x2B) {
658       /* (most) common case 1.3 (iso.org) */
659       *oid_ptr = 1;
660       oid_ptr++;
661       *oid_ptr = 3;
662       oid_ptr++;
663     } else if (data < 40) {
664       *oid_ptr = 0;
665       oid_ptr++;
666       *oid_ptr = data;
667       oid_ptr++;
668     } else if (data < 80) {
669       *oid_ptr = 1;
670       oid_ptr++;
671       *oid_ptr = data - 40;
672       oid_ptr++;
673     } else {
674       *oid_ptr = 2;
675       oid_ptr++;
676       *oid_ptr = data - 80;
677       oid_ptr++;
678     }
679     *oid_len = 2;
680   } else {
681     /* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */
682     return ERR_OK;
683   }
684 
685   while ((len > 0) && (*oid_len < oid_max_len)) {
686     PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
687     len--;
688 
689     if ((data & 0x80) == 0x00) {
690       /* sub-identifier uses single octet */
691       *oid_ptr = data;
692     } else {
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));
697         len--;
698 
699         sub_id = (sub_id << 7) + (data & ~0x80);
700       }
701 
702       if ((data & 0x80) != 0) {
703         /* "more bytes following" bit still set at end of len */
704         return ERR_VAL;
705       }
706       *oid_ptr = sub_id;
707     }
708     oid_ptr++;
709     (*oid_len)++;
710   }
711 
712   if (len > 0) {
713     /* OID to long to fit in our buffer */
714     return ERR_MEM;
715   }
716 
717   return ERR_OK;
718 }
719 
720 /**
721  * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
722  * from incoming message into array.
723  *
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
730  */
731 err_t
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)
733 {
734   if (len > buf_max_len) {
735     /* not enough dst space */
736     return ERR_MEM;
737   }
738   *buf_len = len;
739 
740   while (len > 0) {
741     PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf));
742     buf++;
743     len--;
744   }
745 
746   return ERR_OK;
747 }
748 
749 #endif /* LWIP_SNMP */
750