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
snmp_ans1_enc_tlv(struct snmp_pbuf_stream * pbuf_stream,struct snmp_asn1_tlv * tlv)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
snmp_asn1_enc_raw(struct snmp_pbuf_stream * pbuf_stream,const u8_t * raw,u16_t raw_len)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
snmp_asn1_enc_u32t(struct snmp_pbuf_stream * pbuf_stream,u16_t octets_needed,u32_t value)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
snmp_asn1_enc_u64t(struct snmp_pbuf_stream * pbuf_stream,u16_t octets_needed,const u32_t * value)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
snmp_asn1_enc_s32t(struct snmp_pbuf_stream * pbuf_stream,u16_t octets_needed,s32_t value)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
snmp_asn1_enc_oid(struct snmp_pbuf_stream * pbuf_stream,const u32_t * oid,u16_t oid_len)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
snmp_asn1_enc_length_cnt(u16_t length,u8_t * octets_needed)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
snmp_asn1_enc_u32t_cnt(u32_t value,u16_t * octets_needed)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
snmp_asn1_enc_u64t_cnt(const u32_t * value,u16_t * octets_needed)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
snmp_asn1_enc_s32t_cnt(s32_t value,u16_t * octets_needed)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
snmp_asn1_enc_oid_cnt(const u32_t * oid,u16_t oid_len,u16_t * octets_needed)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
snmp_asn1_dec_tlv(struct snmp_pbuf_stream * pbuf_stream,struct snmp_asn1_tlv * tlv)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
snmp_asn1_dec_u32t(struct snmp_pbuf_stream * pbuf_stream,u16_t len,u32_t * value)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
snmp_asn1_dec_u64t(struct snmp_pbuf_stream * pbuf_stream,u16_t len,u32_t * value)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
snmp_asn1_dec_s32t(struct snmp_pbuf_stream * pbuf_stream,u16_t len,s32_t * value)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
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)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
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)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