Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Release 1.0
[palacios.git] / geekos / src / lwip / core / snmp / asn1_enc.c
diff --git a/geekos/src/lwip/core/snmp/asn1_enc.c b/geekos/src/lwip/core/snmp/asn1_enc.c
new file mode 100644 (file)
index 0000000..77af6b4
--- /dev/null
@@ -0,0 +1,611 @@
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) encoding
+ *
+ * @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 <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp_asn1.h"
+
+/**
+ * Returns octet count for length.
+ *
+ * @param length
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
+{
+  if (length < 0x80U)
+  {
+    *octets_needed = 1;
+  }
+  else if (length < 0x100U)
+  {
+    *octets_needed = 2;
+  }
+  else
+  {
+    *octets_needed = 3;
+  }
+}
+
+/**
+ * Returns octet count for an u32_t.
+ *
+ * @param value
+ * @param octets_needed points to the return value
+ *
+ * @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!!
+ */
+void
+snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
+{
+  if (value < 0x80UL)
+  {
+    *octets_needed = 1;
+  }
+  else if (value < 0x8000UL)
+  {
+    *octets_needed = 2;
+  }
+  else if (value < 0x800000UL)
+  {
+    *octets_needed = 3;
+  }
+  else if (value < 0x80000000UL)
+  {
+    *octets_needed = 4;
+  }
+  else
+  {
+    *octets_needed = 5;
+  }
+}
+
+/**
+ * Returns octet count for an s32_t.
+ *
+ * @param value
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed.
+ */
+void
+snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
+{
+  if (value < 0)
+  {
+    value = ~value;
+  }
+  if (value < 0x80L)
+  {
+    *octets_needed = 1;
+  }
+  else if (value < 0x8000L)
+  {
+    *octets_needed = 2;
+  }
+  else if (value < 0x800000L)
+  {
+    *octets_needed = 3;
+  }
+  else
+  {
+    *octets_needed = 4;
+  }
+}
+
+/**
+ * Returns octet count for an object identifier.
+ *
+ * @param ident_len object identifier array length
+ * @param ident points to object identifier array
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed)
+{
+  s32_t sub_id;
+  u8_t cnt;
+
+  cnt = 0;
+  if (ident_len > 1)
+  {
+    /* compressed prefix in one octet */
+    cnt++;
+    ident_len -= 2;
+    ident += 2;
+  }
+  while(ident_len > 0)
+  {
+    ident_len--;
+    sub_id = *ident;
+
+    sub_id >>= 7;
+    cnt++;
+    while(sub_id > 0)
+    {
+      sub_id >>= 7;
+      cnt++;
+    }
+    ident++;
+  }
+  *octets_needed = cnt;
+}
+
+/**
+ * Encodes ASN type field into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode value into
+ * @param ofs points to the offset within the pbuf chain
+ * @param type input ASN1 type
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_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;
+      *msg_ptr = type;
+      return ERR_OK;
+    }
+    p = p->next;
+  }
+  /* p == NULL, ofs >= plen */
+  return ERR_ARG;
+}
+
+/**
+ * Encodes host order length field into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode length into
+ * @param ofs points to the offset within the pbuf chain
+ * @param length is the host order length to be encoded
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, 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 (length < 0x80)
+      {
+        *msg_ptr = length;
+        return ERR_OK;
+      }
+      else if (length < 0x100)
+      {
+        *msg_ptr = 0x81;
+        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++;
+        }
+        *msg_ptr = length;
+        return ERR_OK;
+      }
+      else
+      {
+        u8_t i;
+
+        /* length >= 0x100 && length <= 0xFFFF */
+        *msg_ptr = 0x82;
+        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 */
+            *msg_ptr = length;
+          }
+          else
+          {
+            /* most significant length octet */
+            *msg_ptr = length >> 8;
+          }
+        }
+        return ERR_OK;
+      }
+    }
+    p = p->next;
+  }
+  /* p == NULL, ofs >= plen */
+  return ERR_ARG;
+}
+
+/**
+ * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode value into
+ * @param ofs points to the offset within the pbuf chain
+ * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
+ * @param value is the host order u32_t value to be encoded
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_u32t_cnt()
+ */
+err_t
+snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u8_t octets_needed, 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 (octets_needed == 5)
+      {
+        /* not enough bits in 'value' add leading 0x00 */
+        octets_needed--;
+        *msg_ptr = 0x00;
+        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 (octets_needed > 1)
+      {
+        octets_needed--;
+        *msg_ptr = value >> (octets_needed << 3);
+        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++;
+        }
+      }
+      /* (only) one least significant octet */
+      *msg_ptr = value;
+      return ERR_OK;
+    }
+    p = p->next;
+  }
+  /* p == NULL, ofs >= plen */
+  return ERR_ARG;
+}
+
+/**
+ * Encodes s32_t integer into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode value into
+ * @param ofs points to the offset within the pbuf chain
+ * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
+ * @param value is the host order s32_t value to be encoded
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_s32t_cnt()
+ */
+err_t
+snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u8_t octets_needed, s32_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;
+
+      while (octets_needed > 1)
+      {
+        octets_needed--;
+        *msg_ptr = value >> (octets_needed << 3);
+        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++;
+        }
+      }
+      /* (only) one least significant octet */
+      *msg_ptr = value;
+      return ERR_OK;
+    }
+    p = p->next;
+  }
+  /* p == NULL, ofs >= plen */
+  return ERR_ARG;
+}
+
+/**
+ * Encodes object identifier into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode oid into
+ * @param ofs points to the offset within the pbuf chain
+ * @param ident_len object identifier array length
+ * @param ident points to object identifier array
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident)
+{
+  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 (ident_len > 1)
+      {
+        if ((ident[0] == 1) && (ident[1] == 3))
+        {
+          /* compressed (most common) prefix .iso.org */
+          *msg_ptr = 0x2b;
+        }
+        else
+        {
+          /* calculate prefix */
+          *msg_ptr = (ident[0] * 40) + ident[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++;
+        }
+        ident_len -= 2;
+        ident += 2;
+      }
+      else
+      {
+/* @bug:  allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression??  */
+        /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
+        return ERR_ARG;
+      }
+      while (ident_len > 0)
+      {
+        s32_t sub_id;
+        u8_t shift, tail;
+
+        ident_len--;
+        sub_id = *ident;
+        tail = 0;
+        shift = 28;
+        while(shift > 0)
+        {
+          u8_t code;
+
+          code = sub_id >> shift;
+          if ((code != 0) || (tail != 0))
+          {
+            tail = 1;
+            *msg_ptr = code | 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++;
+            }
+          }
+          shift -= 7;
+        }
+        *msg_ptr = (u8_t)sub_id & 0x7F;
+        if (ident_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++;
+          }
+        }
+        /* proceed to next sub-identifier */
+        ident++;
+      }
+      return ERR_OK;
+    }
+    p = p->next;
+  }
+  /* p == NULL, ofs >= plen */
+  return ERR_ARG;
+}
+
+/**
+ * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode raw data into
+ * @param ofs points to the offset within the pbuf chain
+ * @param raw_len raw data length
+ * @param raw points raw data
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u8_t raw_len, u8_t *raw)
+{
+  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;
+
+      while (raw_len > 1)
+      {
+        /* copy raw_len - 1 octets */
+        raw_len--;
+        *msg_ptr = *raw;
+        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++;
+        }
+      }
+      if (raw_len > 0)
+      {
+        /* copy last or single octet */
+        *msg_ptr = *raw;
+      }
+      return ERR_OK;
+    }
+    p = p->next;
+  }
+  /* p == NULL, ofs >= plen */
+  return ERR_ARG;
+}
+
+#endif /* LWIP_SNMP */