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 */
51err_t
52snmp_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 */
84err_t
85snmp_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 */
239err_t
240snmp_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 */
332err_t
333snmp_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 */
428err_t
429snmp_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 */
595err_t
596snmp_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