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