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