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 * Encodes s32_t integer into a pbuf chained ASN1 msg.
174 *
175 * @param pbuf_stream points to a pbuf stream
176 * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
177 * @param value is the host order s32_t value to be encoded
178 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
179 *
180 * @see snmp_asn1_enc_s32t_cnt()
181 */
182 err_t
snmp_asn1_enc_s32t(struct snmp_pbuf_stream * pbuf_stream,u16_t octets_needed,s32_t value)183 snmp_asn1_enc_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, s32_t value)
184 {
185 while (octets_needed > 1) {
186 octets_needed--;
187
188 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
189 }
190
191 /* (only) one least significant octet */
192 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
193
194 return ERR_OK;
195 }
196
197 /**
198 * Encodes object identifier into a pbuf chained ASN1 msg.
199 *
200 * @param pbuf_stream points to a pbuf stream
201 * @param oid points to object identifier array
202 * @param oid_len object identifier array length
203 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
204 */
205 err_t
snmp_asn1_enc_oid(struct snmp_pbuf_stream * pbuf_stream,const u32_t * oid,u16_t oid_len)206 snmp_asn1_enc_oid(struct snmp_pbuf_stream *pbuf_stream, const u32_t *oid, u16_t oid_len)
207 {
208 if (oid_len > 1) {
209 /* write compressed first two sub id's */
210 u32_t compressed_byte = ((oid[0] * 40) + oid[1]);
211 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)compressed_byte));
212 oid_len -= 2;
213 oid += 2;
214 } else {
215 /* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
216 /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
217 return ERR_ARG;
218 }
219
220 while (oid_len > 0) {
221 u32_t sub_id;
222 u8_t shift, tail;
223
224 oid_len--;
225 sub_id = *oid;
226 tail = 0;
227 shift = 28;
228 while (shift > 0) {
229 u8_t code;
230
231 code = (u8_t)(sub_id >> shift);
232 if ((code != 0) || (tail != 0)) {
233 tail = 1;
234 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80));
235 }
236 shift -= 7;
237 }
238 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F));
239
240 /* proceed to next sub-identifier */
241 oid++;
242 }
243 return ERR_OK;
244 }
245
246 /**
247 * Returns octet count for length.
248 *
249 * @param length parameter length
250 * @param octets_needed points to the return value
251 */
252 void
snmp_asn1_enc_length_cnt(u16_t length,u8_t * octets_needed)253 snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
254 {
255 if (length < 0x80U) {
256 *octets_needed = 1;
257 } else if (length < 0x100U) {
258 *octets_needed = 2;
259 } else {
260 *octets_needed = 3;
261 }
262 }
263
264 /**
265 * Returns octet count for an u32_t.
266 *
267 * @param value value to be encoded
268 * @param octets_needed points to the return value
269 *
270 * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
271 * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
272 * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
273 */
274 void
snmp_asn1_enc_u32t_cnt(u32_t value,u16_t * octets_needed)275 snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
276 {
277 if (value < 0x80UL) {
278 *octets_needed = 1;
279 } else if (value < 0x8000UL) {
280 *octets_needed = 2;
281 } else if (value < 0x800000UL) {
282 *octets_needed = 3;
283 } else if (value < 0x80000000UL) {
284 *octets_needed = 4;
285 } else {
286 *octets_needed = 5;
287 }
288 }
289
290 /**
291 * Returns octet count for an s32_t.
292 *
293 * @param value value to be encoded
294 * @param octets_needed points to the return value
295 *
296 * @note ASN coded integers are _always_ signed.
297 */
298 void
snmp_asn1_enc_s32t_cnt(s32_t value,u16_t * octets_needed)299 snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
300 {
301 if (value < 0) {
302 value = ~value;
303 }
304 if (value < 0x80L) {
305 *octets_needed = 1;
306 } else if (value < 0x8000L) {
307 *octets_needed = 2;
308 } else if (value < 0x800000L) {
309 *octets_needed = 3;
310 } else {
311 *octets_needed = 4;
312 }
313 }
314
315 /**
316 * Returns octet count for an object identifier.
317 *
318 * @param oid points to object identifier array
319 * @param oid_len object identifier array length
320 * @param octets_needed points to the return value
321 */
322 void
snmp_asn1_enc_oid_cnt(const u32_t * oid,u16_t oid_len,u16_t * octets_needed)323 snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed)
324 {
325 u32_t sub_id;
326
327 *octets_needed = 0;
328 if (oid_len > 1) {
329 /* compressed prefix in one octet */
330 (*octets_needed)++;
331 oid_len -= 2;
332 oid += 2;
333 }
334 while (oid_len > 0) {
335 oid_len--;
336 sub_id = *oid;
337
338 sub_id >>= 7;
339 (*octets_needed)++;
340 while (sub_id > 0) {
341 sub_id >>= 7;
342 (*octets_needed)++;
343 }
344 oid++;
345 }
346 }
347
348 /**
349 * Decodes a TLV from a pbuf stream.
350 *
351 * @param pbuf_stream points to a pbuf stream
352 * @param tlv returns decoded TLV
353 * @return ERR_OK if successful, ERR_VAL if we can't decode
354 */
355 err_t
snmp_asn1_dec_tlv(struct snmp_pbuf_stream * pbuf_stream,struct snmp_asn1_tlv * tlv)356 snmp_asn1_dec_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv)
357 {
358 u8_t data;
359
360 /* decode type first */
361 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
362 tlv->type = data;
363
364 if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
365 /* extended format is not used by SNMP so we do not accept those values */
366 return ERR_VAL;
367 }
368 tlv->type_len = 1;
369
370 /* now, decode length */
371 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
372
373 if (data < 0x80) { /* short form */
374 tlv->length_len = 1;
375 tlv->value_len = data;
376 } else if (data > 0x80) { /* long form */
377 u8_t length_bytes = data - 0x80;
378 if (length_bytes > pbuf_stream->length) {
379 return ERR_VAL;
380 }
381 tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */
382 tlv->value_len = 0;
383
384 while (length_bytes > 0) {
385 /* we only support up to u16.maxvalue-1 (2 bytes) but have to accept leading zero bytes */
386 if (tlv->value_len > 0xFF) {
387 return ERR_VAL;
388 }
389 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
390 tlv->value_len <<= 8;
391 tlv->value_len |= data;
392
393 /* take care for special value used for indefinite length */
394 if (tlv->value_len == 0xFFFF) {
395 return ERR_VAL;
396 }
397
398 length_bytes--;
399 }
400 } else { /* data == 0x80 indefinite length form */
401 /* (not allowed for SNMP; RFC 1157, 3.2.2) */
402 return ERR_VAL;
403 }
404
405 return ERR_OK;
406 }
407
408 /**
409 * Decodes positive integer (counter, gauge, timeticks) into u32_t.
410 *
411 * @param pbuf_stream points to a pbuf stream
412 * @param len length of the coded integer field
413 * @param value return host order integer
414 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
415 *
416 * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
417 * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
418 * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
419 */
420 err_t
snmp_asn1_dec_u32t(struct snmp_pbuf_stream * pbuf_stream,u16_t len,u32_t * value)421 snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
422 {
423 u8_t data;
424
425 if ((len > 0) && (len <= 5)) {
426 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
427
428 /* expecting sign bit to be zero, only unsigned please! */
429 if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) {
430 *value = data;
431 len--;
432
433 while (len > 0) {
434 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
435 len--;
436
437 *value <<= 8;
438 *value |= data;
439 }
440
441 return ERR_OK;
442 }
443 }
444
445 return ERR_VAL;
446 }
447
448 /**
449 * Decodes integer into s32_t.
450 *
451 * @param pbuf_stream points to a pbuf stream
452 * @param len length of the coded integer field
453 * @param value return host order integer
454 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
455 *
456 * @note ASN coded integers are _always_ signed!
457 */
458 err_t
snmp_asn1_dec_s32t(struct snmp_pbuf_stream * pbuf_stream,u16_t len,s32_t * value)459 snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value)
460 {
461 u8_t data;
462
463 if ((len > 0) && (len < 5)) {
464 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
465
466 if (data & 0x80) {
467 /* negative, start from -1 */
468 *value = -1;
469 *value = (*value << 8) | data;
470 } else {
471 /* positive, start from 0 */
472 *value = data;
473 }
474 len--;
475 /* shift in the remaining value */
476 while (len > 0) {
477 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
478 *value = (*value << 8) | data;
479 len--;
480 }
481 return ERR_OK;
482 }
483
484 return ERR_VAL;
485 }
486
487 /**
488 * Decodes object identifier from incoming message into array of u32_t.
489 *
490 * @param pbuf_stream points to a pbuf stream
491 * @param len length of the coded object identifier
492 * @param oid return decoded object identifier
493 * @param oid_len return decoded object identifier length
494 * @param oid_max_len size of oid buffer
495 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
496 */
497 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)498 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)
499 {
500 u32_t *oid_ptr;
501 u8_t data;
502
503 *oid_len = 0;
504 oid_ptr = oid;
505 if (len > 0) {
506 if (oid_max_len < 2) {
507 return ERR_MEM;
508 }
509
510 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
511 len--;
512
513 /* first compressed octet */
514 if (data == 0x2B) {
515 /* (most) common case 1.3 (iso.org) */
516 *oid_ptr = 1;
517 oid_ptr++;
518 *oid_ptr = 3;
519 oid_ptr++;
520 } else if (data < 40) {
521 *oid_ptr = 0;
522 oid_ptr++;
523 *oid_ptr = data;
524 oid_ptr++;
525 } else if (data < 80) {
526 *oid_ptr = 1;
527 oid_ptr++;
528 *oid_ptr = data - 40;
529 oid_ptr++;
530 } else {
531 *oid_ptr = 2;
532 oid_ptr++;
533 *oid_ptr = data - 80;
534 oid_ptr++;
535 }
536 *oid_len = 2;
537 } else {
538 /* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */
539 return ERR_OK;
540 }
541
542 while ((len > 0) && (*oid_len < oid_max_len)) {
543 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
544 len--;
545
546 if ((data & 0x80) == 0x00) {
547 /* sub-identifier uses single octet */
548 *oid_ptr = data;
549 } else {
550 /* sub-identifier uses multiple octets */
551 u32_t sub_id = (data & ~0x80);
552 while ((len > 0) && ((data & 0x80) != 0)) {
553 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
554 len--;
555
556 sub_id = (sub_id << 7) + (data & ~0x80);
557 }
558
559 if ((data & 0x80) != 0) {
560 /* "more bytes following" bit still set at end of len */
561 return ERR_VAL;
562 }
563 *oid_ptr = sub_id;
564 }
565 oid_ptr++;
566 (*oid_len)++;
567 }
568
569 if (len > 0) {
570 /* OID to long to fit in our buffer */
571 return ERR_MEM;
572 }
573
574 return ERR_OK;
575 }
576
577 /**
578 * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
579 * from incoming message into array.
580 *
581 * @param pbuf_stream points to a pbuf stream
582 * @param len length of the coded raw data (zero is valid, e.g. empty string!)
583 * @param buf return raw bytes
584 * @param buf_len returns length of the raw return value
585 * @param buf_max_len buffer size
586 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
587 */
588 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)589 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)
590 {
591 if (len > buf_max_len) {
592 /* not enough dst space */
593 return ERR_MEM;
594 }
595 *buf_len = len;
596
597 while (len > 0) {
598 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf));
599 buf++;
600 len--;
601 }
602
603 return ERR_OK;
604 }
605
606 #if LWIP_HAVE_INT64
607 /**
608 * Returns octet count for an u64_t.
609 *
610 * @param value value to be encoded
611 * @param octets_needed points to the return value
612 *
613 * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
614 * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
615 * of 0xFFFFFFFFFFFFFFFF is preceded with 0x00 and the length is 9 octets!!
616 */
617 void
snmp_asn1_enc_u64t_cnt(u64_t value,u16_t * octets_needed)618 snmp_asn1_enc_u64t_cnt(u64_t value, u16_t *octets_needed)
619 {
620 /* check if high u32 is 0 */
621 if ((value >> 32) == 0) {
622 /* only low u32 is important */
623 snmp_asn1_enc_u32t_cnt((u32_t)value, octets_needed);
624 } else {
625 /* low u32 does not matter for length determination */
626 snmp_asn1_enc_u32t_cnt((u32_t)(value >> 32), octets_needed);
627 *octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */
628 }
629 }
630
631 /**
632 * Decodes large positive integer (counter64) into 2x u32_t.
633 *
634 * @param pbuf_stream points to a pbuf stream
635 * @param len length of the coded integer field
636 * @param value return 64 bit integer
637 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
638 *
639 * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
640 * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
641 * of 0xFFFFFFFFFFFFFFFF is preceded with 0x00 and the length is 9 octets!!
642 */
643 err_t
snmp_asn1_dec_u64t(struct snmp_pbuf_stream * pbuf_stream,u16_t len,u64_t * value)644 snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u64_t *value)
645 {
646 u8_t data;
647
648 if ((len > 0) && (len <= 9)) {
649 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
650
651 /* expecting sign bit to be zero, only unsigned please! */
652 if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) {
653 *value = data;
654 len--;
655
656 while (len > 0) {
657 PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
658 *value <<= 8;
659 *value |= data;
660 len--;
661 }
662
663 return ERR_OK;
664 }
665 }
666
667 return ERR_VAL;
668 }
669
670 /**
671 * Encodes u64_t (counter64) into a pbuf chained ASN1 msg.
672 *
673 * @param pbuf_stream points to a pbuf stream
674 * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
675 * @param value is the value to be encoded
676 * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
677 *
678 * @see snmp_asn1_enc_u64t_cnt()
679 */
680 err_t
snmp_asn1_enc_u64t(struct snmp_pbuf_stream * pbuf_stream,u16_t octets_needed,u64_t value)681 snmp_asn1_enc_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u64_t value)
682 {
683 if (octets_needed > 9) {
684 return ERR_ARG;
685 }
686 if (octets_needed == 9) {
687 /* not enough bits in 'value' add leading 0x00 */
688 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
689 octets_needed--;
690 }
691
692 while (octets_needed > 1) {
693 octets_needed--;
694 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
695 }
696
697 /* always write at least one octet (also in case of value == 0) */
698 PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value)));
699
700 return ERR_OK;
701 }
702 #endif
703
704 #endif /* LWIP_SNMP */
705