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