• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * @file
3  * Abstract Syntax Notation One (ISO 8824, 8825) decoding
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  */
36 
37 #include "lwip/opt.h"
38 
39 #if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
40 
41 #include "lwip/snmp_asn1.h"
42 
43 /**
44  * Retrieves type field from incoming pbuf chain.
45  *
46  * @param p points to a pbuf holding an ASN1 coded type field
47  * @param ofs points to the offset within the pbuf chain of the ASN1 coded type field
48  * @param type return ASN1 type
49  * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
50  */
51 err_t
snmp_asn1_dec_type(struct pbuf * p,u16_t ofs,u8_t * type)52 snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type)
53 {
54   u16_t plen, base;
55   u8_t *msg_ptr;
56 
57   plen = 0;
58   while (p != NULL)
59   {
60     base = plen;
61     plen += p->len;
62     if (ofs < plen)
63     {
64       msg_ptr = (u8_t*)p->payload;
65       msg_ptr += ofs - base;
66       *type = *msg_ptr;
67       return ERR_OK;
68     }
69     p = p->next;
70   }
71   /* p == NULL, ofs >= plen */
72   return ERR_ARG;
73 }
74 
75 /**
76  * Decodes length field from incoming pbuf chain into host length.
77  *
78  * @param p points to a pbuf holding an ASN1 coded length
79  * @param ofs points to the offset within the pbuf chain of the ASN1 coded length
80  * @param octets_used returns number of octets used by the length code
81  * @param length return host order length, upto 64k
82  * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
83  */
84 err_t
snmp_asn1_dec_length(struct pbuf * p,u16_t ofs,u8_t * octets_used,u16_t * length)85 snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length)
86 {
87   u16_t plen, base;
88   u8_t *msg_ptr;
89 
90   plen = 0;
91   while (p != NULL)
92   {
93     base = plen;
94     plen += p->len;
95     if (ofs < plen)
96     {
97       msg_ptr = (u8_t*)p->payload;
98       msg_ptr += ofs - base;
99 
100       if (*msg_ptr < 0x80)
101       {
102         /* primitive definite length format */
103         *octets_used = 1;
104         *length = *msg_ptr;
105         return ERR_OK;
106       }
107       else if (*msg_ptr == 0x80)
108       {
109         /* constructed indefinite length format, termination with two zero octets */
110         u8_t zeros;
111         u8_t i;
112 
113         *length = 0;
114         zeros = 0;
115         while (zeros != 2)
116         {
117           i = 2;
118           while (i > 0)
119           {
120             i--;
121             (*length) += 1;
122             ofs += 1;
123             if (ofs >= plen)
124             {
125               /* next octet in next pbuf */
126               p = p->next;
127               if (p == NULL) { return ERR_ARG; }
128               msg_ptr = (u8_t*)p->payload;
129               plen += p->len;
130             }
131             else
132             {
133               /* next octet in same pbuf */
134               msg_ptr++;
135             }
136             if (*msg_ptr == 0)
137             {
138               zeros++;
139               if (zeros == 2)
140               {
141                 /* stop while (i > 0) */
142                 i = 0;
143               }
144             }
145             else
146             {
147               zeros = 0;
148             }
149           }
150         }
151         *octets_used = 1;
152         return ERR_OK;
153       }
154       else if (*msg_ptr == 0x81)
155       {
156         /* constructed definite length format, one octet */
157         ofs += 1;
158         if (ofs >= plen)
159         {
160           /* next octet in next pbuf */
161           p = p->next;
162           if (p == NULL) { return ERR_ARG; }
163           msg_ptr = (u8_t*)p->payload;
164         }
165         else
166         {
167           /* next octet in same pbuf */
168           msg_ptr++;
169         }
170         *length = *msg_ptr;
171         *octets_used = 2;
172         return ERR_OK;
173       }
174       else if (*msg_ptr == 0x82)
175       {
176         u8_t i;
177 
178         /* constructed definite length format, two octets */
179         i = 2;
180         while (i > 0)
181         {
182           i--;
183           ofs += 1;
184           if (ofs >= plen)
185           {
186             /* next octet in next pbuf */
187             p = p->next;
188             if (p == NULL) { return ERR_ARG; }
189             msg_ptr = (u8_t*)p->payload;
190             plen += p->len;
191           }
192           else
193           {
194             /* next octet in same pbuf */
195             msg_ptr++;
196           }
197           if (i == 0)
198           {
199             /* least significant length octet */
200             *length |= *msg_ptr;
201           }
202           else
203           {
204             /* most significant length octet */
205             *length = (*msg_ptr) << 8;
206           }
207         }
208         *octets_used = 3;
209         return ERR_OK;
210       }
211       else
212       {
213         /* constructed definite length format 3..127 octets, this is too big (>64k) */
214         /**  @todo: do we need to accept inefficient codings with many leading zero's? */
215         *octets_used = 1 + ((*msg_ptr) & 0x7f);
216         return ERR_ARG;
217       }
218     }
219     p = p->next;
220   }
221 
222   /* p == NULL, ofs >= plen */
223   return ERR_ARG;
224 }
225 
226 /**
227  * Decodes positive integer (counter, gauge, timeticks) into u32_t.
228  *
229  * @param p points to a pbuf holding an ASN1 coded integer
230  * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
231  * @param len length of the coded integer field
232  * @param value return host order integer
233  * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
234  *
235  * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
236  * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
237  * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
238  */
239 err_t
snmp_asn1_dec_u32t(struct pbuf * p,u16_t ofs,u16_t len,u32_t * value)240 snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value)
241 {
242   u16_t plen, base;
243   u8_t *msg_ptr;
244 
245   plen = 0;
246   while (p != NULL)
247   {
248     base = plen;
249     plen += p->len;
250     if (ofs < plen)
251     {
252       msg_ptr = (u8_t*)p->payload;
253       msg_ptr += ofs - base;
254       if ((len > 0) && (len < 6))
255       {
256         /* start from zero */
257         *value = 0;
258         if (*msg_ptr & 0x80)
259         {
260           /* negative, expecting zero sign bit! */
261           return ERR_ARG;
262         }
263         else
264         {
265           /* positive */
266           if ((len > 1) && (*msg_ptr == 0))
267           {
268             /* skip leading "sign byte" octet 0x00 */
269             len--;
270             ofs += 1;
271             if (ofs >= plen)
272             {
273               /* next octet in next pbuf */
274               p = p->next;
275               if (p == NULL) { return ERR_ARG; }
276               msg_ptr = (u8_t*)p->payload;
277               plen += p->len;
278             }
279             else
280             {
281               /* next octet in same pbuf */
282               msg_ptr++;
283             }
284           }
285         }
286         /* OR octets with value */
287         while (len > 1)
288         {
289           len--;
290           *value |= *msg_ptr;
291           *value <<= 8;
292           ofs += 1;
293           if (ofs >= plen)
294           {
295             /* next octet in next pbuf */
296             p = p->next;
297             if (p == NULL) { return ERR_ARG; }
298             msg_ptr = (u8_t*)p->payload;
299             plen += p->len;
300           }
301           else
302           {
303             /* next octet in same pbuf */
304             msg_ptr++;
305           }
306         }
307         *value |= *msg_ptr;
308         return ERR_OK;
309       }
310       else
311       {
312         return ERR_ARG;
313       }
314     }
315     p = p->next;
316   }
317   /* p == NULL, ofs >= plen */
318   return ERR_ARG;
319 }
320 
321 /**
322  * Decodes integer into s32_t.
323  *
324  * @param p points to a pbuf holding an ASN1 coded integer
325  * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
326  * @param len length of the coded integer field
327  * @param value return host order integer
328  * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
329  *
330  * @note ASN coded integers are _always_ signed!
331  */
332 err_t
snmp_asn1_dec_s32t(struct pbuf * p,u16_t ofs,u16_t len,s32_t * value)333 snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value)
334 {
335   u16_t plen, base;
336   u8_t *msg_ptr;
337 #if BYTE_ORDER == LITTLE_ENDIAN
338   u8_t *lsb_ptr = (u8_t*)value;
339 #endif
340 #if BYTE_ORDER == BIG_ENDIAN
341   u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
342 #endif
343   u8_t sign;
344 
345   plen = 0;
346   while (p != NULL)
347   {
348     base = plen;
349     plen += p->len;
350     if (ofs < plen)
351     {
352       msg_ptr = (u8_t*)p->payload;
353       msg_ptr += ofs - base;
354       if ((len > 0) && (len < 5))
355       {
356         if (*msg_ptr & 0x80)
357         {
358           /* negative, start from -1 */
359           *value = -1;
360           sign = 1;
361         }
362         else
363         {
364           /* positive, start from 0 */
365           *value = 0;
366           sign = 0;
367         }
368         /* OR/AND octets with value */
369         while (len > 1)
370         {
371           len--;
372           if (sign)
373           {
374             *lsb_ptr &= *msg_ptr;
375             *value <<= 8;
376             *lsb_ptr |= 255;
377           }
378           else
379           {
380             *lsb_ptr |= *msg_ptr;
381             *value <<= 8;
382           }
383           ofs += 1;
384           if (ofs >= plen)
385           {
386             /* next octet in next pbuf */
387             p = p->next;
388             if (p == NULL) { return ERR_ARG; }
389             msg_ptr = (u8_t*)p->payload;
390             plen += p->len;
391           }
392           else
393           {
394             /* next octet in same pbuf */
395             msg_ptr++;
396           }
397         }
398         if (sign)
399         {
400           *lsb_ptr &= *msg_ptr;
401         }
402         else
403         {
404           *lsb_ptr |= *msg_ptr;
405         }
406         return ERR_OK;
407       }
408       else
409       {
410         return ERR_ARG;
411       }
412     }
413     p = p->next;
414   }
415   /* p == NULL, ofs >= plen */
416   return ERR_ARG;
417 }
418 
419 /**
420  * Decodes object identifier from incoming message into array of s32_t.
421  *
422  * @param p points to a pbuf holding an ASN1 coded object identifier
423  * @param ofs points to the offset within the pbuf chain of the ASN1 coded object identifier
424  * @param len length of the coded object identifier
425  * @param oid return object identifier struct
426  * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
427  */
428 err_t
snmp_asn1_dec_oid(struct pbuf * p,u16_t ofs,u16_t len,struct snmp_obj_id * oid)429 snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid)
430 {
431   u16_t plen, base;
432   u8_t *msg_ptr;
433   s32_t *oid_ptr;
434 
435   plen = 0;
436   while (p != NULL)
437   {
438     base = plen;
439     plen += p->len;
440     if (ofs < plen)
441     {
442       msg_ptr = (u8_t*)p->payload;
443       msg_ptr += ofs - base;
444 
445       oid->len = 0;
446       oid_ptr = &oid->id[0];
447       if (len > 0)
448       {
449         /* first compressed octet */
450         if (*msg_ptr == 0x2B)
451         {
452           /* (most) common case 1.3 (iso.org) */
453           *oid_ptr = 1;
454           oid_ptr++;
455           *oid_ptr = 3;
456           oid_ptr++;
457         }
458         else if (*msg_ptr < 40)
459         {
460           *oid_ptr = 0;
461           oid_ptr++;
462           *oid_ptr = *msg_ptr;
463           oid_ptr++;
464         }
465         else if (*msg_ptr < 80)
466         {
467           *oid_ptr = 1;
468           oid_ptr++;
469           *oid_ptr = (*msg_ptr) - 40;
470           oid_ptr++;
471         }
472         else
473         {
474           *oid_ptr = 2;
475           oid_ptr++;
476           *oid_ptr = (*msg_ptr) - 80;
477           oid_ptr++;
478         }
479         oid->len = 2;
480       }
481       else
482       {
483         /* accepting zero length identifiers e.g. for
484            getnext operation. uncommon but valid */
485         return ERR_OK;
486       }
487       len--;
488       if (len > 0)
489       {
490         ofs += 1;
491         if (ofs >= plen)
492         {
493           /* next octet in next pbuf */
494           p = p->next;
495           if (p == NULL) { return ERR_ARG; }
496           msg_ptr = (u8_t*)p->payload;
497           plen += p->len;
498         }
499         else
500         {
501           /* next octet in same pbuf */
502           msg_ptr++;
503         }
504       }
505       while ((len > 0) && (oid->len < LWIP_SNMP_OBJ_ID_LEN))
506       {
507         /* sub-identifier uses multiple octets */
508         if (*msg_ptr & 0x80)
509         {
510           s32_t sub_id = 0;
511 
512           while ((*msg_ptr & 0x80) && (len > 1))
513           {
514             len--;
515             sub_id = (sub_id << 7) + (*msg_ptr & ~0x80);
516             ofs += 1;
517             if (ofs >= plen)
518             {
519               /* next octet in next pbuf */
520               p = p->next;
521               if (p == NULL) { return ERR_ARG; }
522               msg_ptr = (u8_t*)p->payload;
523               plen += p->len;
524             }
525             else
526             {
527               /* next octet in same pbuf */
528               msg_ptr++;
529             }
530           }
531           if (!(*msg_ptr & 0x80) && (len > 0))
532           {
533             /* last octet sub-identifier */
534             len--;
535             sub_id = (sub_id << 7) + *msg_ptr;
536             *oid_ptr = sub_id;
537           }
538         }
539         else
540         {
541           /* !(*msg_ptr & 0x80) sub-identifier uses single octet */
542           len--;
543           *oid_ptr = *msg_ptr;
544         }
545         if (len > 0)
546         {
547           /* remaining oid bytes available ... */
548           ofs += 1;
549           if (ofs >= plen)
550           {
551             /* next octet in next pbuf */
552             p = p->next;
553             if (p == NULL) { return ERR_ARG; }
554             msg_ptr = (u8_t*)p->payload;
555             plen += p->len;
556           }
557           else
558           {
559             /* next octet in same pbuf */
560             msg_ptr++;
561           }
562         }
563         oid_ptr++;
564         oid->len++;
565       }
566       if (len == 0)
567       {
568         /* len == 0, end of oid */
569         return ERR_OK;
570       }
571       else
572       {
573         /* len > 0, oid->len == LWIP_SNMP_OBJ_ID_LEN or malformed encoding */
574         return ERR_ARG;
575       }
576 
577     }
578     p = p->next;
579   }
580   /* p == NULL, ofs >= plen */
581   return ERR_ARG;
582 }
583 
584 /**
585  * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
586  * from incoming message into array.
587  *
588  * @param p points to a pbuf holding an ASN1 coded raw data
589  * @param ofs points to the offset within the pbuf chain of the ASN1 coded raw data
590  * @param len length of the coded raw data (zero is valid, e.g. empty string!)
591  * @param raw_len length of the raw return value
592  * @param raw return raw bytes
593  * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
594  */
595 err_t
snmp_asn1_dec_raw(struct pbuf * p,u16_t ofs,u16_t len,u16_t raw_len,u8_t * raw)596 snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw)
597 {
598   u16_t plen, base;
599   u8_t *msg_ptr;
600 
601   if (len > 0)
602   {
603     plen = 0;
604     while (p != NULL)
605     {
606       base = plen;
607       plen += p->len;
608       if (ofs < plen)
609       {
610         msg_ptr = (u8_t*)p->payload;
611         msg_ptr += ofs - base;
612         if (raw_len >= len)
613         {
614           while (len > 1)
615           {
616             /* copy len - 1 octets */
617             len--;
618             *raw = *msg_ptr;
619             raw++;
620             ofs += 1;
621             if (ofs >= plen)
622             {
623               /* next octet in next pbuf */
624               p = p->next;
625               if (p == NULL) { return ERR_ARG; }
626               msg_ptr = (u8_t*)p->payload;
627               plen += p->len;
628             }
629             else
630             {
631               /* next octet in same pbuf */
632               msg_ptr++;
633             }
634           }
635           /* copy last octet */
636           *raw = *msg_ptr;
637           return ERR_OK;
638         }
639         else
640         {
641           /* raw_len < len, not enough dst space */
642           return ERR_ARG;
643         }
644       }
645       p = p->next;
646     }
647     /* p == NULL, ofs >= plen */
648     return ERR_ARG;
649   }
650   else
651   {
652     /* len == 0, empty string */
653     return ERR_OK;
654   }
655 }
656 
657 #endif /* LWIP_SNMP */
658