www.pudn.com > lwip-1.3.0.rar > asn1_dec.c


/** 
 * @file 
 * Abstract Syntax Notation One (ISO 8824, 8825) decoding 
 * 
 * @todo not optimised (yet), favor correctness over speed, favor speed over size 
 */ 
 
/* 
 * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. 
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met: 
 * 
 * 1. Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution. 
 * 3. The name of the author may not be used to endorse or promote products 
 *    derived from this software without specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
 * OF SUCH DAMAGE. 
 * 
 * Author: Christiaan Simons  
 */ 
 
#include "lwip/opt.h" 
 
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ 
 
#include "lwip/snmp_asn1.h" 
 
/** 
 * Retrieves type field from incoming pbuf chain. 
 * 
 * @param p points to a pbuf holding an ASN1 coded type field 
 * @param ofs points to the offset within the pbuf chain of the ASN1 coded type field 
 * @param type return ASN1 type 
 * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode 
 */ 
err_t 
snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type) 
{ 
  u16_t plen, base; 
  u8_t *msg_ptr; 
 
  plen = 0; 
  while (p != NULL) 
  { 
    base = plen; 
    plen += p->len; 
    if (ofs < plen) 
    { 
      msg_ptr = p->payload; 
      msg_ptr += ofs - base; 
      *type = *msg_ptr; 
      return ERR_OK; 
    } 
    p = p->next; 
  } 
  /* p == NULL, ofs >= plen */ 
  return ERR_ARG; 
} 
 
/** 
 * Decodes length field from incoming pbuf chain into host length. 
 * 
 * @param p points to a pbuf holding an ASN1 coded length 
 * @param ofs points to the offset within the pbuf chain of the ASN1 coded length 
 * @param octets_used returns number of octets used by the length code 
 * @param length return host order length, upto 64k 
 * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode 
 */ 
err_t 
snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length) 
{ 
  u16_t plen, base; 
  u8_t *msg_ptr; 
 
  plen = 0; 
  while (p != NULL) 
  { 
    base = plen; 
    plen += p->len; 
    if (ofs < plen) 
    { 
      msg_ptr = p->payload; 
      msg_ptr += ofs - base; 
 
      if (*msg_ptr < 0x80) 
      { 
        /* primitive definite length format */ 
        *octets_used = 1; 
        *length = *msg_ptr; 
        return ERR_OK; 
      } 
      else if (*msg_ptr == 0x80) 
      { 
        /* constructed indefinite length format, termination with two zero octets */ 
        u8_t zeros; 
        u8_t i; 
 
        *length = 0; 
        zeros = 0; 
        while (zeros != 2) 
        { 
          i = 2; 
          while (i > 0) 
          { 
            i--; 
            (*length) += 1; 
            ofs += 1; 
            if (ofs >= plen) 
            { 
              /* next octet in next pbuf */ 
              p = p->next; 
              if (p == NULL) { return ERR_ARG; } 
              msg_ptr = p->payload; 
              plen += p->len; 
            } 
            else 
            { 
              /* next octet in same pbuf */ 
              msg_ptr++; 
            } 
            if (*msg_ptr == 0) 
            { 
              zeros++; 
              if (zeros == 2) 
              { 
                /* stop while (i > 0) */ 
                i = 0; 
              } 
            } 
            else 
            { 
              zeros = 0; 
            } 
          } 
        } 
        *octets_used = 1; 
        return ERR_OK; 
      } 
      else if (*msg_ptr == 0x81) 
      { 
        /* constructed definite length format, one octet */ 
        ofs += 1; 
        if (ofs >= plen) 
        { 
          /* next octet in next pbuf */ 
          p = p->next; 
          if (p == NULL) { return ERR_ARG; } 
          msg_ptr = p->payload; 
        } 
        else 
        { 
          /* next octet in same pbuf */ 
          msg_ptr++; 
        } 
        *length = *msg_ptr; 
        *octets_used = 2; 
        return ERR_OK; 
      } 
      else if (*msg_ptr == 0x82) 
      { 
        u8_t i; 
 
        /* constructed definite length format, two octets */ 
        i = 2; 
        while (i > 0) 
        { 
          i--; 
          ofs += 1; 
          if (ofs >= plen) 
          { 
            /* next octet in next pbuf */ 
            p = p->next; 
            if (p == NULL) { return ERR_ARG; } 
            msg_ptr = p->payload; 
            plen += p->len; 
          } 
          else 
          { 
            /* next octet in same pbuf */ 
            msg_ptr++; 
          } 
          if (i == 0) 
          { 
            /* least significant length octet */ 
            *length |= *msg_ptr; 
          } 
          else 
          { 
            /* most significant length octet */ 
            *length = (*msg_ptr) << 8; 
          } 
        } 
        *octets_used = 3; 
        return ERR_OK; 
      } 
      else 
      { 
        /* constructed definite length format 3..127 octets, this is too big (>64k) */ 
        /**  @todo: do we need to accept inefficient codings with many leading zero's? */ 
        *octets_used = 1 + ((*msg_ptr) & 0x7f); 
        return ERR_ARG; 
      } 
    } 
    p = p->next; 
  } 
 
  /* p == NULL, ofs >= plen */ 
  return ERR_ARG; 
} 
 
/** 
 * Decodes positive integer (counter, gauge, timeticks) into u32_t. 
 * 
 * @param p points to a pbuf holding an ASN1 coded integer 
 * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer 
 * @param len length of the coded integer field 
 * @param value return host order integer 
 * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode 
 * 
 * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded 
 * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value 
 * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! 
 */ 
err_t 
snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value) 
{ 
  u16_t plen, base; 
  u8_t *msg_ptr; 
 
  plen = 0; 
  while (p != NULL) 
  { 
    base = plen; 
    plen += p->len; 
    if (ofs < plen) 
    { 
      msg_ptr = p->payload; 
      msg_ptr += ofs - base; 
      if ((len > 0) && (len < 6)) 
      { 
        /* start from zero */ 
        *value = 0; 
        if (*msg_ptr & 0x80) 
        { 
          /* negative, expecting zero sign bit! */ 
          return ERR_ARG; 
        } 
        else 
        { 
          /* positive */ 
          if ((len > 1) && (*msg_ptr == 0)) 
          { 
            /* skip leading "sign byte" octet 0x00 */ 
            len--; 
            ofs += 1; 
            if (ofs >= plen) 
            { 
              /* next octet in next pbuf */ 
              p = p->next; 
              if (p == NULL) { return ERR_ARG; } 
              msg_ptr = p->payload; 
              plen += p->len; 
            } 
            else 
            { 
              /* next octet in same pbuf */ 
              msg_ptr++; 
            } 
          } 
        } 
        /* OR octets with value */ 
        while (len > 1) 
        { 
          len--; 
          *value |= *msg_ptr; 
          *value <<= 8; 
          ofs += 1; 
          if (ofs >= plen) 
          { 
            /* next octet in next pbuf */ 
            p = p->next; 
            if (p == NULL) { return ERR_ARG; } 
            msg_ptr = p->payload; 
            plen += p->len; 
          } 
          else 
          { 
            /* next octet in same pbuf */ 
            msg_ptr++; 
          } 
        } 
        *value |= *msg_ptr; 
        return ERR_OK; 
      } 
      else 
      { 
        return ERR_ARG; 
      } 
    } 
    p = p->next; 
  } 
  /* p == NULL, ofs >= plen */ 
  return ERR_ARG; 
} 
 
/** 
 * Decodes integer into s32_t. 
 * 
 * @param p points to a pbuf holding an ASN1 coded integer 
 * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer 
 * @param len length of the coded integer field 
 * @param value return host order integer 
 * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode 
 * 
 * @note ASN coded integers are _always_ signed! 
 */ 
err_t 
snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value) 
{ 
  u16_t plen, base; 
  u8_t *msg_ptr; 
#if BYTE_ORDER == LITTLE_ENDIAN 
  u8_t *lsb_ptr = (u8_t*)value; 
#endif 
#if BYTE_ORDER == BIG_ENDIAN 
  u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1; 
#endif 
  u8_t sign; 
 
  plen = 0; 
  while (p != NULL) 
  { 
    base = plen; 
    plen += p->len; 
    if (ofs < plen) 
    { 
      msg_ptr = p->payload; 
      msg_ptr += ofs - base; 
      if ((len > 0) && (len < 5)) 
      { 
        if (*msg_ptr & 0x80) 
        { 
          /* negative, start from -1 */ 
          *value = -1; 
          sign = 1; 
        } 
        else 
        { 
          /* positive, start from 0 */ 
          *value = 0; 
          sign = 0; 
        } 
        /* OR/AND octets with value */ 
        while (len > 1) 
        { 
          len--; 
          if (sign) 
          { 
            *lsb_ptr &= *msg_ptr; 
            *value <<= 8; 
            *lsb_ptr |= 255; 
          } 
          else 
          { 
            *lsb_ptr |= *msg_ptr; 
            *value <<= 8; 
          } 
          ofs += 1; 
          if (ofs >= plen) 
          { 
            /* next octet in next pbuf */ 
            p = p->next; 
            if (p == NULL) { return ERR_ARG; } 
            msg_ptr = p->payload; 
            plen += p->len; 
          } 
          else 
          { 
            /* next octet in same pbuf */ 
            msg_ptr++; 
          } 
        } 
        if (sign) 
        { 
          *lsb_ptr &= *msg_ptr; 
        } 
        else 
        { 
          *lsb_ptr |= *msg_ptr; 
        } 
        return ERR_OK; 
      } 
      else 
      { 
        return ERR_ARG; 
      } 
    } 
    p = p->next; 
  } 
  /* p == NULL, ofs >= plen */ 
  return ERR_ARG; 
} 
 
/** 
 * Decodes object identifier from incoming message into array of s32_t. 
 * 
 * @param p points to a pbuf holding an ASN1 coded object identifier 
 * @param ofs points to the offset within the pbuf chain of the ASN1 coded object identifier 
 * @param len length of the coded object identifier 
 * @param oid return object identifier struct 
 * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode 
 */ 
err_t 
snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid) 
{ 
  u16_t plen, base; 
  u8_t *msg_ptr; 
  s32_t *oid_ptr; 
 
  plen = 0; 
  while (p != NULL) 
  { 
    base = plen; 
    plen += p->len; 
    if (ofs < plen) 
    { 
      msg_ptr = p->payload; 
      msg_ptr += ofs - base; 
 
      oid->len = 0; 
      oid_ptr = &oid->id[0]; 
      if (len > 0) 
      { 
        /* first compressed octet */ 
        if (*msg_ptr == 0x2B) 
        { 
          /* (most) common case 1.3 (iso.org) */ 
          *oid_ptr = 1; 
          oid_ptr++; 
          *oid_ptr = 3; 
          oid_ptr++; 
        } 
        else if (*msg_ptr < 40) 
        { 
          *oid_ptr = 0; 
          oid_ptr++; 
          *oid_ptr = *msg_ptr; 
          oid_ptr++; 
        } 
        else if (*msg_ptr < 80) 
        { 
          *oid_ptr = 1; 
          oid_ptr++; 
          *oid_ptr = (*msg_ptr) - 40; 
          oid_ptr++; 
        } 
        else 
        { 
          *oid_ptr = 2; 
          oid_ptr++; 
          *oid_ptr = (*msg_ptr) - 80; 
          oid_ptr++; 
        } 
        oid->len = 2; 
      } 
      else 
      { 
        /* accepting zero length identifiers e.g. for 
           getnext operation. uncommon but valid */ 
        return ERR_OK; 
      } 
      len--; 
      if (len > 0) 
      { 
        ofs += 1; 
        if (ofs >= plen) 
        { 
          /* next octet in next pbuf */ 
          p = p->next; 
          if (p == NULL) { return ERR_ARG; } 
          msg_ptr = p->payload; 
          plen += p->len; 
        } 
        else 
        { 
          /* next octet in same pbuf */ 
          msg_ptr++; 
        } 
      } 
      while ((len > 0) && (oid->len < LWIP_SNMP_OBJ_ID_LEN)) 
      { 
        /* sub-identifier uses multiple octets */ 
        if (*msg_ptr & 0x80) 
        { 
          s32_t sub_id = 0; 
 
          while ((*msg_ptr & 0x80) && (len > 1)) 
          { 
            len--; 
            sub_id = (sub_id << 7) + (*msg_ptr & ~0x80); 
            ofs += 1; 
            if (ofs >= plen) 
            { 
              /* next octet in next pbuf */ 
              p = p->next; 
              if (p == NULL) { return ERR_ARG; } 
              msg_ptr = p->payload; 
              plen += p->len; 
            } 
            else 
            { 
              /* next octet in same pbuf */ 
              msg_ptr++; 
            } 
          } 
          if (!(*msg_ptr & 0x80) && (len > 0)) 
          { 
            /* last octet sub-identifier */ 
            len--; 
            sub_id = (sub_id << 7) + *msg_ptr; 
            *oid_ptr = sub_id; 
          } 
        } 
        else 
        { 
          /* !(*msg_ptr & 0x80) sub-identifier uses single octet */ 
          len--; 
          *oid_ptr = *msg_ptr; 
        } 
        if (len > 0) 
        { 
          /* remaining oid bytes available ... */ 
          ofs += 1; 
          if (ofs >= plen) 
          { 
            /* next octet in next pbuf */ 
            p = p->next; 
            if (p == NULL) { return ERR_ARG; } 
            msg_ptr = p->payload; 
            plen += p->len; 
          } 
          else 
          { 
            /* next octet in same pbuf */ 
            msg_ptr++; 
          } 
        } 
        oid_ptr++; 
        oid->len++; 
      } 
      if (len == 0) 
      { 
        /* len == 0, end of oid */ 
        return ERR_OK; 
      } 
      else 
      { 
        /* len > 0, oid->len == LWIP_SNMP_OBJ_ID_LEN or malformed encoding */ 
        return ERR_ARG; 
      } 
 
    } 
    p = p->next; 
  } 
  /* p == NULL, ofs >= plen */ 
  return ERR_ARG; 
} 
 
/** 
 * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding) 
 * from incoming message into array. 
 * 
 * @param p points to a pbuf holding an ASN1 coded raw data 
 * @param ofs points to the offset within the pbuf chain of the ASN1 coded raw data 
 * @param len length of the coded raw data (zero is valid, e.g. empty string!) 
 * @param raw_len length of the raw return value 
 * @param raw return raw bytes 
 * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode 
 */ 
err_t 
snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw) 
{ 
  u16_t plen, base; 
  u8_t *msg_ptr; 
 
  if (len > 0) 
  { 
    plen = 0; 
    while (p != NULL) 
    { 
      base = plen; 
      plen += p->len; 
      if (ofs < plen) 
      { 
        msg_ptr = p->payload; 
        msg_ptr += ofs - base; 
        if (raw_len >= len) 
        { 
          while (len > 1) 
          { 
            /* copy len - 1 octets */ 
            len--; 
            *raw = *msg_ptr; 
            raw++; 
            ofs += 1; 
            if (ofs >= plen) 
            { 
              /* next octet in next pbuf */ 
              p = p->next; 
              if (p == NULL) { return ERR_ARG; } 
              msg_ptr = p->payload; 
              plen += p->len; 
            } 
            else 
            { 
              /* next octet in same pbuf */ 
              msg_ptr++; 
            } 
          } 
          /* copy last octet */ 
          *raw = *msg_ptr; 
          return ERR_OK; 
        } 
        else 
        { 
          /* raw_len < len, not enough dst space */ 
          return ERR_ARG; 
        } 
      } 
      p = p->next; 
    } 
    /* p == NULL, ofs >= plen */ 
    return ERR_ARG; 
  } 
  else 
  { 
    /* len == 0, empty string */ 
    return ERR_OK; 
  } 
} 
 
#endif /* LWIP_SNMP */