Home | History | Annotate | Download | only in snmp
      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 (at) 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
     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
     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
    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
    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
    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
    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