xref: /nrf52832-nimble/rt-thread/components/net/lwip-2.1.0/src/apps/snmp/snmp_asn1.c (revision 104654410c56c573564690304ae786df310c91fc)
1*10465441SEvalZero /**
2*10465441SEvalZero  * @file
3*10465441SEvalZero  * Abstract Syntax Notation One (ISO 8824, 8825) encoding
4*10465441SEvalZero  *
5*10465441SEvalZero  * @todo not optimised (yet), favor correctness over speed, favor speed over size
6*10465441SEvalZero  */
7*10465441SEvalZero 
8*10465441SEvalZero /*
9*10465441SEvalZero  * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
10*10465441SEvalZero  * All rights reserved.
11*10465441SEvalZero  *
12*10465441SEvalZero  * Redistribution and use in source and binary forms, with or without modification,
13*10465441SEvalZero  * are permitted provided that the following conditions are met:
14*10465441SEvalZero  *
15*10465441SEvalZero  * 1. Redistributions of source code must retain the above copyright notice,
16*10465441SEvalZero  *    this list of conditions and the following disclaimer.
17*10465441SEvalZero  * 2. Redistributions in binary form must reproduce the above copyright notice,
18*10465441SEvalZero  *    this list of conditions and the following disclaimer in the documentation
19*10465441SEvalZero  *    and/or other materials provided with the distribution.
20*10465441SEvalZero  * 3. The name of the author may not be used to endorse or promote products
21*10465441SEvalZero  *    derived from this software without specific prior written permission.
22*10465441SEvalZero  *
23*10465441SEvalZero  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
24*10465441SEvalZero  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25*10465441SEvalZero  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
26*10465441SEvalZero  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27*10465441SEvalZero  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
28*10465441SEvalZero  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29*10465441SEvalZero  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30*10465441SEvalZero  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
31*10465441SEvalZero  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
32*10465441SEvalZero  * OF SUCH DAMAGE.
33*10465441SEvalZero  *
34*10465441SEvalZero  * Author: Christiaan Simons <[email protected]>
35*10465441SEvalZero  *         Martin Hentschel <[email protected]>
36*10465441SEvalZero  */
37*10465441SEvalZero 
38*10465441SEvalZero #include "lwip/apps/snmp_opts.h"
39*10465441SEvalZero 
40*10465441SEvalZero #if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
41*10465441SEvalZero 
42*10465441SEvalZero #include "snmp_asn1.h"
43*10465441SEvalZero 
44*10465441SEvalZero #define PBUF_OP_EXEC(code) \
45*10465441SEvalZero   if ((code) != ERR_OK) { \
46*10465441SEvalZero     return ERR_BUF; \
47*10465441SEvalZero   }
48*10465441SEvalZero 
49*10465441SEvalZero /**
50*10465441SEvalZero  * Encodes a TLV into a pbuf stream.
51*10465441SEvalZero  *
52*10465441SEvalZero  * @param pbuf_stream points to a pbuf stream
53*10465441SEvalZero  * @param tlv TLV to encode
54*10465441SEvalZero  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
55*10465441SEvalZero  */
56*10465441SEvalZero err_t
snmp_ans1_enc_tlv(struct snmp_pbuf_stream * pbuf_stream,struct snmp_asn1_tlv * tlv)57*10465441SEvalZero snmp_ans1_enc_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv)
58*10465441SEvalZero {
59*10465441SEvalZero   u8_t data;
60*10465441SEvalZero   u8_t length_bytes_required;
61*10465441SEvalZero 
62*10465441SEvalZero   /* write type */
63*10465441SEvalZero   if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
64*10465441SEvalZero     /* extended format is not used by SNMP so we do not accept those values */
65*10465441SEvalZero     return ERR_ARG;
66*10465441SEvalZero   }
67*10465441SEvalZero   if (tlv->type_len != 0) {
68*10465441SEvalZero     /* any other value as auto is not accepted for type (we always use one byte because extended syntax is prohibited) */
69*10465441SEvalZero     return ERR_ARG;
70*10465441SEvalZero   }
71*10465441SEvalZero 
72*10465441SEvalZero   PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, tlv->type));
73*10465441SEvalZero   tlv->type_len = 1;
74*10465441SEvalZero 
75*10465441SEvalZero   /* write length */
76*10465441SEvalZero   if (tlv->value_len <= 127) {
77*10465441SEvalZero     length_bytes_required = 1;
78*10465441SEvalZero   } else if (tlv->value_len <= 255) {
79*10465441SEvalZero     length_bytes_required = 2;
80*10465441SEvalZero   } else  {
81*10465441SEvalZero     length_bytes_required = 3;
82*10465441SEvalZero   }
83*10465441SEvalZero 
84*10465441SEvalZero   /* check for forced min length */
85*10465441SEvalZero   if (tlv->length_len > 0) {
86*10465441SEvalZero     if (tlv->length_len < length_bytes_required) {
87*10465441SEvalZero       /* unable to code requested length in requested number of bytes */
88*10465441SEvalZero       return ERR_ARG;
89*10465441SEvalZero     }
90*10465441SEvalZero 
91*10465441SEvalZero     length_bytes_required = tlv->length_len;
92*10465441SEvalZero   } else {
93*10465441SEvalZero     tlv->length_len = length_bytes_required;
94*10465441SEvalZero   }
95*10465441SEvalZero 
96*10465441SEvalZero   if (length_bytes_required > 1) {
97*10465441SEvalZero     /* multi byte representation required */
98*10465441SEvalZero     length_bytes_required--;
99*10465441SEvalZero     data = 0x80 | length_bytes_required; /* extended length definition, 1 length byte follows */
100*10465441SEvalZero 
101*10465441SEvalZero     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
102*10465441SEvalZero 
103*10465441SEvalZero     while (length_bytes_required > 1) {
104*10465441SEvalZero       if (length_bytes_required == 2) {
105*10465441SEvalZero         /* append high byte */
106*10465441SEvalZero         data = (u8_t)(tlv->value_len >> 8);
107*10465441SEvalZero       } else {
108*10465441SEvalZero         /* append leading 0x00 */
109*10465441SEvalZero         data = 0x00;
110*10465441SEvalZero       }
111*10465441SEvalZero 
112*10465441SEvalZero       PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
113*10465441SEvalZero       length_bytes_required--;
114*10465441SEvalZero     }
115*10465441SEvalZero   }
116*10465441SEvalZero 
117*10465441SEvalZero   /* append low byte */
118*10465441SEvalZero   data = (u8_t)(tlv->value_len & 0xFF);
119*10465441SEvalZero   PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
120*10465441SEvalZero 
121*10465441SEvalZero   return ERR_OK;
122*10465441SEvalZero }
123*10465441SEvalZero 
124*10465441SEvalZero /**
125*10465441SEvalZero  * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
126*10465441SEvalZero  *
127*10465441SEvalZero  * @param pbuf_stream points to a pbuf stream
128*10465441SEvalZero  * @param raw_len raw data length
129*10465441SEvalZero  * @param raw points raw data
130*10465441SEvalZero  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
131*10465441SEvalZero  */
132*10465441SEvalZero err_t
snmp_asn1_enc_raw(struct snmp_pbuf_stream * pbuf_stream,const u8_t * raw,u16_t raw_len)133*10465441SEvalZero snmp_asn1_enc_raw(struct snmp_pbuf_stream *pbuf_stream, const u8_t *raw, u16_t raw_len)
134*10465441SEvalZero {
135*10465441SEvalZero   PBUF_OP_EXEC(snmp_pbuf_stream_writebuf(pbuf_stream, raw, raw_len));
136*10465441SEvalZero 
137*10465441SEvalZero   return ERR_OK;
138*10465441SEvalZero }
139*10465441SEvalZero 
140*10465441SEvalZero /**
141*10465441SEvalZero  * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
142*10465441SEvalZero  *
143*10465441SEvalZero  * @param pbuf_stream points to a pbuf stream
144*10465441SEvalZero  * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
145*10465441SEvalZero  * @param value is the host order u32_t value to be encoded
146*10465441SEvalZero  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
147*10465441SEvalZero  *
148*10465441SEvalZero  * @see snmp_asn1_enc_u32t_cnt()
149*10465441SEvalZero  */
150*10465441SEvalZero err_t
snmp_asn1_enc_u32t(struct snmp_pbuf_stream * pbuf_stream,u16_t octets_needed,u32_t value)151*10465441SEvalZero snmp_asn1_enc_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u32_t value)
152*10465441SEvalZero {
153*10465441SEvalZero   if (octets_needed > 5) {
154*10465441SEvalZero     return ERR_ARG;
155*10465441SEvalZero   }
156*10465441SEvalZero   if (octets_needed == 5) {
157*10465441SEvalZero     /* not enough bits in 'value' add leading 0x00 */
158*10465441SEvalZero     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
159*10465441SEvalZero     octets_needed--;
160*10465441SEvalZero   }
161*10465441SEvalZero 
162*10465441SEvalZero   while (octets_needed > 1) {
163*10465441SEvalZero     octets_needed--;
164*10465441SEvalZero     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
165*10465441SEvalZero   }
166*10465441SEvalZero 
167*10465441SEvalZero   /* (only) one least significant octet */
168*10465441SEvalZero   PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
169*10465441SEvalZero 
170*10465441SEvalZero   return ERR_OK;
171*10465441SEvalZero }
172*10465441SEvalZero /**
173*10465441SEvalZero  * Encodes s32_t integer into a pbuf chained ASN1 msg.
174*10465441SEvalZero  *
175*10465441SEvalZero  * @param pbuf_stream points to a pbuf stream
176*10465441SEvalZero  * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
177*10465441SEvalZero  * @param value is the host order s32_t value to be encoded
178*10465441SEvalZero  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
179*10465441SEvalZero  *
180*10465441SEvalZero  * @see snmp_asn1_enc_s32t_cnt()
181*10465441SEvalZero  */
182*10465441SEvalZero err_t
snmp_asn1_enc_s32t(struct snmp_pbuf_stream * pbuf_stream,u16_t octets_needed,s32_t value)183*10465441SEvalZero snmp_asn1_enc_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, s32_t value)
184*10465441SEvalZero {
185*10465441SEvalZero   while (octets_needed > 1) {
186*10465441SEvalZero     octets_needed--;
187*10465441SEvalZero 
188*10465441SEvalZero     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
189*10465441SEvalZero   }
190*10465441SEvalZero 
191*10465441SEvalZero   /* (only) one least significant octet */
192*10465441SEvalZero   PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
193*10465441SEvalZero 
194*10465441SEvalZero   return ERR_OK;
195*10465441SEvalZero }
196*10465441SEvalZero 
197*10465441SEvalZero /**
198*10465441SEvalZero  * Encodes object identifier into a pbuf chained ASN1 msg.
199*10465441SEvalZero  *
200*10465441SEvalZero  * @param pbuf_stream points to a pbuf stream
201*10465441SEvalZero  * @param oid points to object identifier array
202*10465441SEvalZero  * @param oid_len object identifier array length
203*10465441SEvalZero  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
204*10465441SEvalZero  */
205*10465441SEvalZero err_t
snmp_asn1_enc_oid(struct snmp_pbuf_stream * pbuf_stream,const u32_t * oid,u16_t oid_len)206*10465441SEvalZero snmp_asn1_enc_oid(struct snmp_pbuf_stream *pbuf_stream, const u32_t *oid, u16_t oid_len)
207*10465441SEvalZero {
208*10465441SEvalZero   if (oid_len > 1) {
209*10465441SEvalZero     /* write compressed first two sub id's */
210*10465441SEvalZero     u32_t compressed_byte = ((oid[0] * 40) + oid[1]);
211*10465441SEvalZero     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)compressed_byte));
212*10465441SEvalZero     oid_len -= 2;
213*10465441SEvalZero     oid += 2;
214*10465441SEvalZero   } else {
215*10465441SEvalZero     /* @bug:  allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
216*10465441SEvalZero     /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
217*10465441SEvalZero     return ERR_ARG;
218*10465441SEvalZero   }
219*10465441SEvalZero 
220*10465441SEvalZero   while (oid_len > 0) {
221*10465441SEvalZero     u32_t sub_id;
222*10465441SEvalZero     u8_t shift, tail;
223*10465441SEvalZero 
224*10465441SEvalZero     oid_len--;
225*10465441SEvalZero     sub_id = *oid;
226*10465441SEvalZero     tail = 0;
227*10465441SEvalZero     shift = 28;
228*10465441SEvalZero     while (shift > 0) {
229*10465441SEvalZero       u8_t code;
230*10465441SEvalZero 
231*10465441SEvalZero       code = (u8_t)(sub_id >> shift);
232*10465441SEvalZero       if ((code != 0) || (tail != 0)) {
233*10465441SEvalZero         tail = 1;
234*10465441SEvalZero         PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80));
235*10465441SEvalZero       }
236*10465441SEvalZero       shift -= 7;
237*10465441SEvalZero     }
238*10465441SEvalZero     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F));
239*10465441SEvalZero 
240*10465441SEvalZero     /* proceed to next sub-identifier */
241*10465441SEvalZero     oid++;
242*10465441SEvalZero   }
243*10465441SEvalZero   return ERR_OK;
244*10465441SEvalZero }
245*10465441SEvalZero 
246*10465441SEvalZero /**
247*10465441SEvalZero  * Returns octet count for length.
248*10465441SEvalZero  *
249*10465441SEvalZero  * @param length parameter length
250*10465441SEvalZero  * @param octets_needed points to the return value
251*10465441SEvalZero  */
252*10465441SEvalZero void
snmp_asn1_enc_length_cnt(u16_t length,u8_t * octets_needed)253*10465441SEvalZero snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
254*10465441SEvalZero {
255*10465441SEvalZero   if (length < 0x80U) {
256*10465441SEvalZero     *octets_needed = 1;
257*10465441SEvalZero   } else if (length < 0x100U) {
258*10465441SEvalZero     *octets_needed = 2;
259*10465441SEvalZero   } else {
260*10465441SEvalZero     *octets_needed = 3;
261*10465441SEvalZero   }
262*10465441SEvalZero }
263*10465441SEvalZero 
264*10465441SEvalZero /**
265*10465441SEvalZero  * Returns octet count for an u32_t.
266*10465441SEvalZero  *
267*10465441SEvalZero  * @param value value to be encoded
268*10465441SEvalZero  * @param octets_needed points to the return value
269*10465441SEvalZero  *
270*10465441SEvalZero  * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
271*10465441SEvalZero  * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
272*10465441SEvalZero  * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
273*10465441SEvalZero  */
274*10465441SEvalZero void
snmp_asn1_enc_u32t_cnt(u32_t value,u16_t * octets_needed)275*10465441SEvalZero snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
276*10465441SEvalZero {
277*10465441SEvalZero   if (value < 0x80UL) {
278*10465441SEvalZero     *octets_needed = 1;
279*10465441SEvalZero   } else if (value < 0x8000UL) {
280*10465441SEvalZero     *octets_needed = 2;
281*10465441SEvalZero   } else if (value < 0x800000UL) {
282*10465441SEvalZero     *octets_needed = 3;
283*10465441SEvalZero   } else if (value < 0x80000000UL) {
284*10465441SEvalZero     *octets_needed = 4;
285*10465441SEvalZero   } else {
286*10465441SEvalZero     *octets_needed = 5;
287*10465441SEvalZero   }
288*10465441SEvalZero }
289*10465441SEvalZero 
290*10465441SEvalZero /**
291*10465441SEvalZero  * Returns octet count for an s32_t.
292*10465441SEvalZero  *
293*10465441SEvalZero  * @param value value to be encoded
294*10465441SEvalZero  * @param octets_needed points to the return value
295*10465441SEvalZero  *
296*10465441SEvalZero  * @note ASN coded integers are _always_ signed.
297*10465441SEvalZero  */
298*10465441SEvalZero void
snmp_asn1_enc_s32t_cnt(s32_t value,u16_t * octets_needed)299*10465441SEvalZero snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
300*10465441SEvalZero {
301*10465441SEvalZero   if (value < 0) {
302*10465441SEvalZero     value = ~value;
303*10465441SEvalZero   }
304*10465441SEvalZero   if (value < 0x80L) {
305*10465441SEvalZero     *octets_needed = 1;
306*10465441SEvalZero   } else if (value < 0x8000L) {
307*10465441SEvalZero     *octets_needed = 2;
308*10465441SEvalZero   } else if (value < 0x800000L) {
309*10465441SEvalZero     *octets_needed = 3;
310*10465441SEvalZero   } else {
311*10465441SEvalZero     *octets_needed = 4;
312*10465441SEvalZero   }
313*10465441SEvalZero }
314*10465441SEvalZero 
315*10465441SEvalZero /**
316*10465441SEvalZero  * Returns octet count for an object identifier.
317*10465441SEvalZero  *
318*10465441SEvalZero  * @param oid points to object identifier array
319*10465441SEvalZero  * @param oid_len object identifier array length
320*10465441SEvalZero  * @param octets_needed points to the return value
321*10465441SEvalZero  */
322*10465441SEvalZero void
snmp_asn1_enc_oid_cnt(const u32_t * oid,u16_t oid_len,u16_t * octets_needed)323*10465441SEvalZero snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed)
324*10465441SEvalZero {
325*10465441SEvalZero   u32_t sub_id;
326*10465441SEvalZero 
327*10465441SEvalZero   *octets_needed = 0;
328*10465441SEvalZero   if (oid_len > 1) {
329*10465441SEvalZero     /* compressed prefix in one octet */
330*10465441SEvalZero     (*octets_needed)++;
331*10465441SEvalZero     oid_len -= 2;
332*10465441SEvalZero     oid += 2;
333*10465441SEvalZero   }
334*10465441SEvalZero   while (oid_len > 0) {
335*10465441SEvalZero     oid_len--;
336*10465441SEvalZero     sub_id = *oid;
337*10465441SEvalZero 
338*10465441SEvalZero     sub_id >>= 7;
339*10465441SEvalZero     (*octets_needed)++;
340*10465441SEvalZero     while (sub_id > 0) {
341*10465441SEvalZero       sub_id >>= 7;
342*10465441SEvalZero       (*octets_needed)++;
343*10465441SEvalZero     }
344*10465441SEvalZero     oid++;
345*10465441SEvalZero   }
346*10465441SEvalZero }
347*10465441SEvalZero 
348*10465441SEvalZero /**
349*10465441SEvalZero  * Decodes a TLV from a pbuf stream.
350*10465441SEvalZero  *
351*10465441SEvalZero  * @param pbuf_stream points to a pbuf stream
352*10465441SEvalZero  * @param tlv returns decoded TLV
353*10465441SEvalZero  * @return ERR_OK if successful, ERR_VAL if we can't decode
354*10465441SEvalZero  */
355*10465441SEvalZero err_t
snmp_asn1_dec_tlv(struct snmp_pbuf_stream * pbuf_stream,struct snmp_asn1_tlv * tlv)356*10465441SEvalZero snmp_asn1_dec_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv)
357*10465441SEvalZero {
358*10465441SEvalZero   u8_t data;
359*10465441SEvalZero 
360*10465441SEvalZero   /* decode type first */
361*10465441SEvalZero   PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
362*10465441SEvalZero   tlv->type = data;
363*10465441SEvalZero 
364*10465441SEvalZero   if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
365*10465441SEvalZero     /* extended format is not used by SNMP so we do not accept those values */
366*10465441SEvalZero     return ERR_VAL;
367*10465441SEvalZero   }
368*10465441SEvalZero   tlv->type_len = 1;
369*10465441SEvalZero 
370*10465441SEvalZero   /* now, decode length */
371*10465441SEvalZero   PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
372*10465441SEvalZero 
373*10465441SEvalZero   if (data < 0x80) { /* short form */
374*10465441SEvalZero     tlv->length_len = 1;
375*10465441SEvalZero     tlv->value_len  = data;
376*10465441SEvalZero   } else if (data > 0x80) { /* long form */
377*10465441SEvalZero     u8_t length_bytes = data - 0x80;
378*10465441SEvalZero     if (length_bytes > pbuf_stream->length) {
379*10465441SEvalZero       return ERR_VAL;
380*10465441SEvalZero     }
381*10465441SEvalZero     tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */
382*10465441SEvalZero     tlv->value_len = 0;
383*10465441SEvalZero 
384*10465441SEvalZero     while (length_bytes > 0) {
385*10465441SEvalZero       /* we only support up to u16.maxvalue-1 (2 bytes) but have to accept leading zero bytes */
386*10465441SEvalZero       if (tlv->value_len > 0xFF) {
387*10465441SEvalZero         return ERR_VAL;
388*10465441SEvalZero       }
389*10465441SEvalZero       PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
390*10465441SEvalZero       tlv->value_len <<= 8;
391*10465441SEvalZero       tlv->value_len |= data;
392*10465441SEvalZero 
393*10465441SEvalZero       /* take care for special value used for indefinite length */
394*10465441SEvalZero       if (tlv->value_len == 0xFFFF) {
395*10465441SEvalZero         return ERR_VAL;
396*10465441SEvalZero       }
397*10465441SEvalZero 
398*10465441SEvalZero       length_bytes--;
399*10465441SEvalZero     }
400*10465441SEvalZero   } else { /* data == 0x80 indefinite length form */
401*10465441SEvalZero     /* (not allowed for SNMP; RFC 1157, 3.2.2) */
402*10465441SEvalZero     return ERR_VAL;
403*10465441SEvalZero   }
404*10465441SEvalZero 
405*10465441SEvalZero   return ERR_OK;
406*10465441SEvalZero }
407*10465441SEvalZero 
408*10465441SEvalZero /**
409*10465441SEvalZero  * Decodes positive integer (counter, gauge, timeticks) into u32_t.
410*10465441SEvalZero  *
411*10465441SEvalZero  * @param pbuf_stream points to a pbuf stream
412*10465441SEvalZero  * @param len length of the coded integer field
413*10465441SEvalZero  * @param value return host order integer
414*10465441SEvalZero  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
415*10465441SEvalZero  *
416*10465441SEvalZero  * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
417*10465441SEvalZero  * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
418*10465441SEvalZero  * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
419*10465441SEvalZero  */
420*10465441SEvalZero err_t
snmp_asn1_dec_u32t(struct snmp_pbuf_stream * pbuf_stream,u16_t len,u32_t * value)421*10465441SEvalZero snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
422*10465441SEvalZero {
423*10465441SEvalZero   u8_t data;
424*10465441SEvalZero 
425*10465441SEvalZero   if ((len > 0) && (len <= 5)) {
426*10465441SEvalZero     PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
427*10465441SEvalZero 
428*10465441SEvalZero     /* expecting sign bit to be zero, only unsigned please! */
429*10465441SEvalZero     if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) {
430*10465441SEvalZero       *value = data;
431*10465441SEvalZero       len--;
432*10465441SEvalZero 
433*10465441SEvalZero       while (len > 0) {
434*10465441SEvalZero         PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
435*10465441SEvalZero         len--;
436*10465441SEvalZero 
437*10465441SEvalZero         *value <<= 8;
438*10465441SEvalZero         *value |= data;
439*10465441SEvalZero       }
440*10465441SEvalZero 
441*10465441SEvalZero       return ERR_OK;
442*10465441SEvalZero     }
443*10465441SEvalZero   }
444*10465441SEvalZero 
445*10465441SEvalZero   return ERR_VAL;
446*10465441SEvalZero }
447*10465441SEvalZero 
448*10465441SEvalZero /**
449*10465441SEvalZero  * Decodes integer into s32_t.
450*10465441SEvalZero  *
451*10465441SEvalZero  * @param pbuf_stream points to a pbuf stream
452*10465441SEvalZero  * @param len length of the coded integer field
453*10465441SEvalZero  * @param value return host order integer
454*10465441SEvalZero  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
455*10465441SEvalZero  *
456*10465441SEvalZero  * @note ASN coded integers are _always_ signed!
457*10465441SEvalZero  */
458*10465441SEvalZero err_t
snmp_asn1_dec_s32t(struct snmp_pbuf_stream * pbuf_stream,u16_t len,s32_t * value)459*10465441SEvalZero snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value)
460*10465441SEvalZero {
461*10465441SEvalZero   u8_t data;
462*10465441SEvalZero 
463*10465441SEvalZero   if ((len > 0) && (len < 5)) {
464*10465441SEvalZero     PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
465*10465441SEvalZero 
466*10465441SEvalZero     if (data & 0x80) {
467*10465441SEvalZero       /* negative, start from -1 */
468*10465441SEvalZero       *value = -1;
469*10465441SEvalZero       *value = (*value << 8) | data;
470*10465441SEvalZero     } else {
471*10465441SEvalZero       /* positive, start from 0 */
472*10465441SEvalZero       *value = data;
473*10465441SEvalZero     }
474*10465441SEvalZero     len--;
475*10465441SEvalZero     /* shift in the remaining value */
476*10465441SEvalZero     while (len > 0) {
477*10465441SEvalZero       PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
478*10465441SEvalZero       *value = (*value << 8) | data;
479*10465441SEvalZero       len--;
480*10465441SEvalZero     }
481*10465441SEvalZero     return ERR_OK;
482*10465441SEvalZero   }
483*10465441SEvalZero 
484*10465441SEvalZero   return ERR_VAL;
485*10465441SEvalZero }
486*10465441SEvalZero 
487*10465441SEvalZero /**
488*10465441SEvalZero  * Decodes object identifier from incoming message into array of u32_t.
489*10465441SEvalZero  *
490*10465441SEvalZero  * @param pbuf_stream points to a pbuf stream
491*10465441SEvalZero  * @param len length of the coded object identifier
492*10465441SEvalZero  * @param oid return decoded object identifier
493*10465441SEvalZero  * @param oid_len return decoded object identifier length
494*10465441SEvalZero  * @param oid_max_len size of oid buffer
495*10465441SEvalZero  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
496*10465441SEvalZero  */
497*10465441SEvalZero 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*10465441SEvalZero 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*10465441SEvalZero {
500*10465441SEvalZero   u32_t *oid_ptr;
501*10465441SEvalZero   u8_t data;
502*10465441SEvalZero 
503*10465441SEvalZero   *oid_len = 0;
504*10465441SEvalZero   oid_ptr = oid;
505*10465441SEvalZero   if (len > 0) {
506*10465441SEvalZero     if (oid_max_len < 2) {
507*10465441SEvalZero       return ERR_MEM;
508*10465441SEvalZero     }
509*10465441SEvalZero 
510*10465441SEvalZero     PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
511*10465441SEvalZero     len--;
512*10465441SEvalZero 
513*10465441SEvalZero     /* first compressed octet */
514*10465441SEvalZero     if (data == 0x2B) {
515*10465441SEvalZero       /* (most) common case 1.3 (iso.org) */
516*10465441SEvalZero       *oid_ptr = 1;
517*10465441SEvalZero       oid_ptr++;
518*10465441SEvalZero       *oid_ptr = 3;
519*10465441SEvalZero       oid_ptr++;
520*10465441SEvalZero     } else if (data < 40) {
521*10465441SEvalZero       *oid_ptr = 0;
522*10465441SEvalZero       oid_ptr++;
523*10465441SEvalZero       *oid_ptr = data;
524*10465441SEvalZero       oid_ptr++;
525*10465441SEvalZero     } else if (data < 80) {
526*10465441SEvalZero       *oid_ptr = 1;
527*10465441SEvalZero       oid_ptr++;
528*10465441SEvalZero       *oid_ptr = data - 40;
529*10465441SEvalZero       oid_ptr++;
530*10465441SEvalZero     } else {
531*10465441SEvalZero       *oid_ptr = 2;
532*10465441SEvalZero       oid_ptr++;
533*10465441SEvalZero       *oid_ptr = data - 80;
534*10465441SEvalZero       oid_ptr++;
535*10465441SEvalZero     }
536*10465441SEvalZero     *oid_len = 2;
537*10465441SEvalZero   } else {
538*10465441SEvalZero     /* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */
539*10465441SEvalZero     return ERR_OK;
540*10465441SEvalZero   }
541*10465441SEvalZero 
542*10465441SEvalZero   while ((len > 0) && (*oid_len < oid_max_len)) {
543*10465441SEvalZero     PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
544*10465441SEvalZero     len--;
545*10465441SEvalZero 
546*10465441SEvalZero     if ((data & 0x80) == 0x00) {
547*10465441SEvalZero       /* sub-identifier uses single octet */
548*10465441SEvalZero       *oid_ptr = data;
549*10465441SEvalZero     } else {
550*10465441SEvalZero       /* sub-identifier uses multiple octets */
551*10465441SEvalZero       u32_t sub_id = (data & ~0x80);
552*10465441SEvalZero       while ((len > 0) && ((data & 0x80) != 0)) {
553*10465441SEvalZero         PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
554*10465441SEvalZero         len--;
555*10465441SEvalZero 
556*10465441SEvalZero         sub_id = (sub_id << 7) + (data & ~0x80);
557*10465441SEvalZero       }
558*10465441SEvalZero 
559*10465441SEvalZero       if ((data & 0x80) != 0) {
560*10465441SEvalZero         /* "more bytes following" bit still set at end of len */
561*10465441SEvalZero         return ERR_VAL;
562*10465441SEvalZero       }
563*10465441SEvalZero       *oid_ptr = sub_id;
564*10465441SEvalZero     }
565*10465441SEvalZero     oid_ptr++;
566*10465441SEvalZero     (*oid_len)++;
567*10465441SEvalZero   }
568*10465441SEvalZero 
569*10465441SEvalZero   if (len > 0) {
570*10465441SEvalZero     /* OID to long to fit in our buffer */
571*10465441SEvalZero     return ERR_MEM;
572*10465441SEvalZero   }
573*10465441SEvalZero 
574*10465441SEvalZero   return ERR_OK;
575*10465441SEvalZero }
576*10465441SEvalZero 
577*10465441SEvalZero /**
578*10465441SEvalZero  * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
579*10465441SEvalZero  * from incoming message into array.
580*10465441SEvalZero  *
581*10465441SEvalZero  * @param pbuf_stream points to a pbuf stream
582*10465441SEvalZero  * @param len length of the coded raw data (zero is valid, e.g. empty string!)
583*10465441SEvalZero  * @param buf return raw bytes
584*10465441SEvalZero  * @param buf_len returns length of the raw return value
585*10465441SEvalZero  * @param buf_max_len buffer size
586*10465441SEvalZero  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
587*10465441SEvalZero  */
588*10465441SEvalZero 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*10465441SEvalZero 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*10465441SEvalZero {
591*10465441SEvalZero   if (len > buf_max_len) {
592*10465441SEvalZero     /* not enough dst space */
593*10465441SEvalZero     return ERR_MEM;
594*10465441SEvalZero   }
595*10465441SEvalZero   *buf_len = len;
596*10465441SEvalZero 
597*10465441SEvalZero   while (len > 0) {
598*10465441SEvalZero     PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf));
599*10465441SEvalZero     buf++;
600*10465441SEvalZero     len--;
601*10465441SEvalZero   }
602*10465441SEvalZero 
603*10465441SEvalZero   return ERR_OK;
604*10465441SEvalZero }
605*10465441SEvalZero 
606*10465441SEvalZero #if LWIP_HAVE_INT64
607*10465441SEvalZero /**
608*10465441SEvalZero  * Returns octet count for an u64_t.
609*10465441SEvalZero  *
610*10465441SEvalZero  * @param value value to be encoded
611*10465441SEvalZero  * @param octets_needed points to the return value
612*10465441SEvalZero  *
613*10465441SEvalZero  * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
614*10465441SEvalZero  * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
615*10465441SEvalZero  * of 0xFFFFFFFFFFFFFFFF is preceded with 0x00 and the length is 9 octets!!
616*10465441SEvalZero  */
617*10465441SEvalZero void
snmp_asn1_enc_u64t_cnt(u64_t value,u16_t * octets_needed)618*10465441SEvalZero snmp_asn1_enc_u64t_cnt(u64_t value, u16_t *octets_needed)
619*10465441SEvalZero {
620*10465441SEvalZero   /* check if high u32 is 0 */
621*10465441SEvalZero   if ((value >> 32) == 0) {
622*10465441SEvalZero     /* only low u32 is important */
623*10465441SEvalZero     snmp_asn1_enc_u32t_cnt((u32_t)value, octets_needed);
624*10465441SEvalZero   } else {
625*10465441SEvalZero     /* low u32 does not matter for length determination */
626*10465441SEvalZero     snmp_asn1_enc_u32t_cnt((u32_t)(value >> 32), octets_needed);
627*10465441SEvalZero     *octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */
628*10465441SEvalZero   }
629*10465441SEvalZero }
630*10465441SEvalZero 
631*10465441SEvalZero /**
632*10465441SEvalZero  * Decodes large positive integer (counter64) into 2x u32_t.
633*10465441SEvalZero  *
634*10465441SEvalZero  * @param pbuf_stream points to a pbuf stream
635*10465441SEvalZero  * @param len length of the coded integer field
636*10465441SEvalZero  * @param value return 64 bit integer
637*10465441SEvalZero  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
638*10465441SEvalZero  *
639*10465441SEvalZero  * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
640*10465441SEvalZero  * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
641*10465441SEvalZero  * of 0xFFFFFFFFFFFFFFFF is preceded with 0x00 and the length is 9 octets!!
642*10465441SEvalZero  */
643*10465441SEvalZero err_t
snmp_asn1_dec_u64t(struct snmp_pbuf_stream * pbuf_stream,u16_t len,u64_t * value)644*10465441SEvalZero snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u64_t *value)
645*10465441SEvalZero {
646*10465441SEvalZero   u8_t data;
647*10465441SEvalZero 
648*10465441SEvalZero   if ((len > 0) && (len <= 9)) {
649*10465441SEvalZero     PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
650*10465441SEvalZero 
651*10465441SEvalZero     /* expecting sign bit to be zero, only unsigned please! */
652*10465441SEvalZero     if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) {
653*10465441SEvalZero       *value = data;
654*10465441SEvalZero       len--;
655*10465441SEvalZero 
656*10465441SEvalZero       while (len > 0) {
657*10465441SEvalZero         PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
658*10465441SEvalZero         *value <<= 8;
659*10465441SEvalZero         *value |= data;
660*10465441SEvalZero         len--;
661*10465441SEvalZero       }
662*10465441SEvalZero 
663*10465441SEvalZero       return ERR_OK;
664*10465441SEvalZero     }
665*10465441SEvalZero   }
666*10465441SEvalZero 
667*10465441SEvalZero   return ERR_VAL;
668*10465441SEvalZero }
669*10465441SEvalZero 
670*10465441SEvalZero /**
671*10465441SEvalZero  * Encodes u64_t (counter64) into a pbuf chained ASN1 msg.
672*10465441SEvalZero  *
673*10465441SEvalZero  * @param pbuf_stream points to a pbuf stream
674*10465441SEvalZero  * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
675*10465441SEvalZero  * @param value is the value to be encoded
676*10465441SEvalZero  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
677*10465441SEvalZero  *
678*10465441SEvalZero  * @see snmp_asn1_enc_u64t_cnt()
679*10465441SEvalZero  */
680*10465441SEvalZero err_t
snmp_asn1_enc_u64t(struct snmp_pbuf_stream * pbuf_stream,u16_t octets_needed,u64_t value)681*10465441SEvalZero snmp_asn1_enc_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u64_t value)
682*10465441SEvalZero {
683*10465441SEvalZero   if (octets_needed > 9) {
684*10465441SEvalZero     return ERR_ARG;
685*10465441SEvalZero   }
686*10465441SEvalZero   if (octets_needed == 9) {
687*10465441SEvalZero     /* not enough bits in 'value' add leading 0x00 */
688*10465441SEvalZero     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
689*10465441SEvalZero     octets_needed--;
690*10465441SEvalZero   }
691*10465441SEvalZero 
692*10465441SEvalZero   while (octets_needed > 1) {
693*10465441SEvalZero     octets_needed--;
694*10465441SEvalZero     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
695*10465441SEvalZero   }
696*10465441SEvalZero 
697*10465441SEvalZero   /* always write at least one octet (also in case of value == 0) */
698*10465441SEvalZero   PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value)));
699*10465441SEvalZero 
700*10465441SEvalZero   return ERR_OK;
701*10465441SEvalZero }
702*10465441SEvalZero #endif
703*10465441SEvalZero 
704*10465441SEvalZero #endif /* LWIP_SNMP */
705