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