Rtos API example

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lwip_snmp_asn1.c Source File

lwip_snmp_asn1.c

Go to the documentation of this file.
00001 /**
00002  * @file
00003  * Abstract Syntax Notation One (ISO 8824, 8825) encoding
00004  *
00005  * @todo not optimised (yet), favor correctness over speed, favor speed over size
00006  */
00007 
00008 /*
00009  * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
00010  * All rights reserved.
00011  *
00012  * Redistribution and use in source and binary forms, with or without modification,
00013  * are permitted provided that the following conditions are met:
00014  *
00015  * 1. Redistributions of source code must retain the above copyright notice,
00016  *    this list of conditions and the following disclaimer.
00017  * 2. Redistributions in binary form must reproduce the above copyright notice,
00018  *    this list of conditions and the following disclaimer in the documentation
00019  *    and/or other materials provided with the distribution.
00020  * 3. The name of the author may not be used to endorse or promote products
00021  *    derived from this software without specific prior written permission.
00022  *
00023  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
00024  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00025  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
00026  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00027  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
00028  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00029  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00030  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
00031  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
00032  * OF SUCH DAMAGE.
00033  *
00034  * Author: Christiaan Simons <christiaan.simons@axon.tv>
00035  *         Martin Hentschel <info@cl-soft.de>
00036  */
00037 
00038 #include "lwip/apps/snmp_opts.h"
00039 
00040 #if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
00041 
00042 #include "snmp_asn1.h"
00043 
00044 #define PBUF_OP_EXEC(code) \
00045   if ((code) != ERR_OK) { \
00046     return ERR_BUF; \
00047   }
00048 
00049 /**
00050  * Encodes a TLV into a pbuf stream.
00051  *
00052  * @param pbuf_stream points to a pbuf stream
00053  * @param tlv TLV to encode
00054  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
00055  */
00056 err_t
00057 snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
00058 {
00059   u8_t data;
00060   u8_t length_bytes_required;
00061 
00062   /* write type */
00063   if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
00064     /* extended format is not used by SNMP so we do not accept those values */
00065     return ERR_ARG;
00066   }
00067   if (tlv->type_len != 0) {
00068     /* any other value as auto is not accepted for type (we always use one byte because extended syntax is prohibited) */
00069     return ERR_ARG;
00070   }
00071 
00072   PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, tlv->type));
00073   tlv->type_len = 1;
00074 
00075   /* write length */
00076   if (tlv->value_len <= 127) {
00077     length_bytes_required = 1;
00078   } else if (tlv->value_len <= 255) {
00079     length_bytes_required = 2;
00080   } else  {
00081     length_bytes_required = 3;
00082   }
00083 
00084   /* check for forced min length */
00085   if (tlv->length_len > 0) {
00086     if (tlv->length_len < length_bytes_required) {
00087       /* unable to code requested length in requested number of bytes */
00088       return ERR_ARG;
00089     }
00090 
00091     length_bytes_required = tlv->length_len;
00092   } else {
00093     tlv->length_len = length_bytes_required;
00094   }
00095 
00096   if (length_bytes_required > 1) {
00097     /* multi byte representation required */
00098     length_bytes_required--;
00099     data = 0x80 | length_bytes_required; /* extended length definition, 1 length byte follows */
00100 
00101     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
00102 
00103     while (length_bytes_required > 1) {
00104       if (length_bytes_required == 2) {
00105         /* append high byte */
00106         data = (u8_t)(tlv->value_len >> 8);
00107       } else {
00108         /* append leading 0x00 */
00109         data = 0x00;
00110       }
00111 
00112       PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
00113       length_bytes_required--;
00114     }
00115   }
00116 
00117   /* append low byte */
00118   data = (u8_t)(tlv->value_len & 0xFF);
00119   PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
00120 
00121   return ERR_OK;
00122 }
00123 
00124 /**
00125  * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
00126  *
00127  * @param pbuf_stream points to a pbuf stream
00128  * @param raw_len raw data length
00129  * @param raw points raw data
00130  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
00131  */
00132 err_t
00133 snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len)
00134 {
00135   PBUF_OP_EXEC(snmp_pbuf_stream_writebuf(pbuf_stream, raw, raw_len));
00136 
00137   return ERR_OK;
00138 }
00139 
00140 /**
00141  * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
00142  *
00143  * @param pbuf_stream points to a pbuf stream
00144  * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
00145  * @param value is the host order u32_t value to be encoded
00146  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
00147  *
00148  * @see snmp_asn1_enc_u32t_cnt()
00149  */
00150 err_t
00151 snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value)
00152 {
00153   if (octets_needed > 5) {
00154     return ERR_ARG;
00155   }
00156   if (octets_needed == 5) {
00157     /* not enough bits in 'value' add leading 0x00 */
00158     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
00159     octets_needed--;
00160   }
00161 
00162   while (octets_needed > 1) {
00163     octets_needed--;
00164     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
00165   }
00166 
00167   /* (only) one least significant octet */
00168   PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
00169 
00170   return ERR_OK;
00171 }
00172 
00173 /**
00174  * Encodes u64_t (counter64) into a pbuf chained ASN1 msg.
00175  *
00176  * @param pbuf_stream points to a pbuf stream
00177  * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
00178  * @param value is the host order u32_t value to be encoded
00179  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
00180  *
00181  * @see snmp_asn1_enc_u64t_cnt()
00182  */
00183 err_t
00184 snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value)
00185 {
00186   if (octets_needed > 9) {
00187     return ERR_ARG;
00188   }
00189   if (octets_needed == 9) {
00190     /* not enough bits in 'value' add leading 0x00 */
00191     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
00192     octets_needed--;
00193   }
00194 
00195   while (octets_needed > 4) {
00196     octets_needed--;
00197     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> ((octets_needed-4) << 3))));
00198   }
00199 
00200   /* skip to low u32 */
00201   value++;
00202 
00203   while (octets_needed > 1) {
00204     octets_needed--;
00205     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> (octets_needed << 3))));
00206   }
00207 
00208   /* always write at least one octet (also in case of value == 0) */
00209   PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value)));
00210 
00211   return ERR_OK;
00212 }
00213 
00214 /**
00215  * Encodes s32_t integer into a pbuf chained ASN1 msg.
00216  *
00217  * @param pbuf_stream points to a pbuf stream
00218  * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
00219  * @param value is the host order s32_t value to be encoded
00220  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
00221  *
00222  * @see snmp_asn1_enc_s32t_cnt()
00223  */
00224 err_t
00225 snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value)
00226 {
00227   while (octets_needed > 1) {
00228     octets_needed--;
00229 
00230     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
00231   }
00232 
00233   /* (only) one least significant octet */
00234   PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
00235 
00236   return ERR_OK;
00237 }
00238 
00239 /**
00240  * Encodes object identifier into a pbuf chained ASN1 msg.
00241  *
00242  * @param pbuf_stream points to a pbuf stream
00243  * @param oid points to object identifier array
00244  * @param oid_len object identifier array length
00245  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
00246  */
00247 err_t
00248 snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len)
00249 {
00250   if (oid_len > 1) {
00251     /* write compressed first two sub id's */
00252     u32_t compressed_byte = ((oid[0] * 40) + oid[1]);
00253     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)compressed_byte));
00254     oid_len -= 2;
00255     oid += 2;
00256   } else {
00257     /* @bug:  allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
00258     /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
00259     return ERR_ARG;
00260   }
00261 
00262   while (oid_len > 0) {
00263     u32_t sub_id;
00264     u8_t shift, tail;
00265 
00266     oid_len--;
00267     sub_id = *oid;
00268     tail = 0;
00269     shift = 28;
00270     while (shift > 0) {
00271       u8_t code;
00272 
00273       code = (u8_t)(sub_id >> shift);
00274       if ((code != 0) || (tail != 0)) {
00275         tail = 1;
00276         PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80));
00277       }
00278       shift -= 7;
00279     }
00280     PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F));
00281 
00282     /* proceed to next sub-identifier */
00283     oid++;
00284   }
00285   return ERR_OK;
00286 }
00287 
00288 /**
00289  * Returns octet count for length.
00290  *
00291  * @param length parameter length
00292  * @param octets_needed points to the return value
00293  */
00294 void
00295 snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
00296 {
00297   if (length < 0x80U) {
00298     *octets_needed = 1;
00299   } else if (length < 0x100U) {
00300     *octets_needed = 2;
00301   } else {
00302     *octets_needed = 3;
00303   }
00304 }
00305 
00306 /**
00307  * Returns octet count for an u32_t.
00308  *
00309  * @param value value to be encoded
00310  * @param octets_needed points to the return value
00311  *
00312  * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
00313  * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
00314  * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
00315  */
00316 void
00317 snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
00318 {
00319   if (value < 0x80UL) {
00320     *octets_needed = 1;
00321   } else if (value < 0x8000UL) {
00322     *octets_needed = 2;
00323   } else if (value < 0x800000UL) {
00324     *octets_needed = 3;
00325   } else if (value < 0x80000000UL) {
00326     *octets_needed = 4;
00327   } else {
00328     *octets_needed = 5;
00329   }
00330 }
00331 
00332 /**
00333  * Returns octet count for an u64_t.
00334  *
00335  * @param value value to be encoded
00336  * @param octets_needed points to the return value
00337  *
00338  * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
00339  * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
00340  * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
00341  */
00342 void
00343 snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed)
00344 {
00345   /* check if high u32 is 0 */
00346   if (*value == 0x00) {
00347     /* only low u32 is important */
00348     value++;
00349     snmp_asn1_enc_u32t_cnt(*value, octets_needed);
00350   } else {
00351     /* low u32 does not matter for length determination */
00352     snmp_asn1_enc_u32t_cnt(*value, octets_needed);
00353     *octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */
00354   }
00355 }
00356 
00357 /**
00358  * Returns octet count for an s32_t.
00359  *
00360  * @param value value to be encoded
00361  * @param octets_needed points to the return value
00362  *
00363  * @note ASN coded integers are _always_ signed.
00364  */
00365 void
00366 snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
00367 {
00368   if (value < 0) {
00369     value = ~value;
00370   }
00371   if (value < 0x80L) {
00372     *octets_needed = 1;
00373   } else if (value < 0x8000L) {
00374     *octets_needed = 2;
00375   } else if (value < 0x800000L) {
00376     *octets_needed = 3;
00377   } else {
00378     *octets_needed = 4;
00379   }
00380 }
00381 
00382 /**
00383  * Returns octet count for an object identifier.
00384  *
00385  * @param oid points to object identifier array
00386  * @param oid_len object identifier array length
00387  * @param octets_needed points to the return value
00388  */
00389 void
00390 snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed)
00391 {
00392   u32_t sub_id;
00393 
00394   *octets_needed = 0;
00395   if (oid_len > 1) {
00396     /* compressed prefix in one octet */
00397     (*octets_needed)++;
00398     oid_len -= 2;
00399     oid += 2;
00400   }
00401   while (oid_len > 0) {
00402     oid_len--;
00403     sub_id = *oid;
00404 
00405     sub_id >>= 7;
00406     (*octets_needed)++;
00407     while (sub_id > 0) {
00408       sub_id >>= 7;
00409       (*octets_needed)++;
00410     }
00411     oid++;
00412   }
00413 }
00414 
00415 /**
00416  * Decodes a TLV from a pbuf stream.
00417  *
00418  * @param pbuf_stream points to a pbuf stream
00419  * @param tlv returns decoded TLV
00420  * @return ERR_OK if successful, ERR_VAL if we can't decode
00421  */
00422 err_t
00423 snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
00424 {
00425   u8_t data;
00426 
00427   /* decode type first */
00428   PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
00429   tlv->type = data;
00430 
00431   if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
00432     /* extended format is not used by SNMP so we do not accept those values */
00433     return ERR_VAL;
00434   }
00435   tlv->type_len = 1;
00436 
00437   /* now, decode length */
00438   PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
00439 
00440   if (data < 0x80) { /* short form */
00441     tlv->length_len = 1;
00442     tlv->value_len  = data;
00443   } else if (data > 0x80) { /* long form */
00444     u8_t length_bytes = data - 0x80;
00445     tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */
00446     tlv->value_len = 0;
00447 
00448     while (length_bytes > 0) {
00449       /* we only support up to u16.maxvalue-1 (2 bytes) but have to accept leading zero bytes */
00450       if (tlv->value_len > 0xFF) {
00451         return ERR_VAL;
00452       }
00453       PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
00454       tlv->value_len <<= 8;
00455       tlv->value_len |= data;
00456 
00457       /* take care for special value used for indefinite length */
00458       if (tlv->value_len == 0xFFFF) {
00459         return ERR_VAL;
00460       }
00461 
00462       length_bytes--;
00463     }
00464   } else { /* data == 0x80 indefinite length form */
00465     /* (not allowed for SNMP; RFC 1157, 3.2.2) */
00466     return ERR_VAL;
00467   }
00468 
00469   return ERR_OK;
00470 }
00471 
00472 /**
00473  * Decodes positive integer (counter, gauge, timeticks) into u32_t.
00474  *
00475  * @param pbuf_stream points to a pbuf stream
00476  * @param len length of the coded integer field
00477  * @param value return host order integer
00478  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
00479  *
00480  * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
00481  * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
00482  * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
00483  */
00484 err_t
00485 snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
00486 {
00487   u8_t data;
00488 
00489   if ((len > 0) && (len <= 5)) {
00490     PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
00491 
00492     /* expecting sign bit to be zero, only unsigned please! */
00493     if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) {
00494       *value = data;
00495       len--;
00496 
00497       while (len > 0) {
00498         PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
00499         len--;
00500 
00501         *value <<= 8;
00502         *value |= data;
00503       }
00504 
00505       return ERR_OK;
00506     }
00507   }
00508 
00509   return ERR_VAL;
00510 }
00511 
00512 /**
00513  * Decodes large positive integer (counter64) into 2x u32_t.
00514  *
00515  * @param pbuf_stream points to a pbuf stream
00516  * @param len length of the coded integer field
00517  * @param value return host order integer
00518  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
00519  *
00520  * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
00521  * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
00522  * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
00523  */
00524 err_t
00525 snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
00526 {
00527   u8_t data;
00528 
00529   if (len <= 4) {
00530     /* high u32 is 0 */
00531     *value = 0;
00532     /* directly skip to low u32 */
00533     value++;
00534   }
00535 
00536   if ((len > 0) && (len <= 9)) {
00537     PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
00538 
00539     /* expecting sign bit to be zero, only unsigned please! */
00540     if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) {
00541       *value = data;
00542       len--;
00543 
00544       while (len > 0) {
00545         PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
00546 
00547         if (len == 4) {
00548           /* skip to low u32 */
00549           value++;
00550           *value = 0;
00551         } else {
00552           *value <<= 8;
00553         }
00554 
00555         *value |= data;
00556         len--;
00557       }
00558 
00559       return ERR_OK;
00560     }
00561   }
00562 
00563   return ERR_VAL;
00564 }
00565 
00566 /**
00567  * Decodes integer into s32_t.
00568  *
00569  * @param pbuf_stream points to a pbuf stream
00570  * @param len length of the coded integer field
00571  * @param value return host order integer
00572  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
00573  *
00574  * @note ASN coded integers are _always_ signed!
00575  */
00576 err_t
00577 snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value)
00578 {
00579 #if BYTE_ORDER == LITTLE_ENDIAN
00580   u8_t *lsb_ptr = (u8_t*)value;
00581 #endif
00582 #if BYTE_ORDER == BIG_ENDIAN
00583   u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
00584 #endif
00585   u8_t sign;
00586   u8_t data;
00587 
00588   if ((len > 0) && (len < 5)) {
00589     PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
00590     len--;
00591 
00592     if (data & 0x80) {
00593       /* negative, start from -1 */
00594       *value = -1;
00595       sign = 1;
00596       *lsb_ptr &= data;
00597     } else {
00598       /* positive, start from 0 */
00599       *value = 0;
00600       sign = 0;
00601       *lsb_ptr |= data;
00602     }
00603 
00604     /* OR/AND octets with value */
00605     while (len > 0) {
00606       PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
00607       len--;
00608 
00609 #if BYTE_ORDER == LITTLE_ENDIAN
00610       *value <<= 8;
00611 #endif
00612 #if BYTE_ORDER == BIG_ENDIAN
00613       *value >>= 8;
00614 #endif
00615 
00616       if (sign) {
00617         *lsb_ptr |= 255;
00618         *lsb_ptr &= data;
00619       } else {
00620         *lsb_ptr |= data;
00621       }
00622     }
00623 
00624     return ERR_OK;
00625   }
00626 
00627   return ERR_VAL;
00628 }
00629 
00630 /**
00631  * Decodes object identifier from incoming message into array of u32_t.
00632  *
00633  * @param pbuf_stream points to a pbuf stream
00634  * @param len length of the coded object identifier
00635  * @param oid return decoded object identifier
00636  * @param oid_len return decoded object identifier length
00637  * @param oid_max_len size of oid buffer
00638  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
00639  */
00640 err_t
00641 snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t* oid, u8_t* oid_len, u8_t oid_max_len)
00642 {
00643   u32_t *oid_ptr;
00644   u8_t data;
00645 
00646   *oid_len = 0;
00647   oid_ptr = oid;
00648   if (len > 0) {
00649     if (oid_max_len < 2) {
00650       return ERR_MEM;
00651     }
00652 
00653     PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
00654     len--;
00655 
00656     /* first compressed octet */
00657     if (data == 0x2B) {
00658       /* (most) common case 1.3 (iso.org) */
00659       *oid_ptr = 1;
00660       oid_ptr++;
00661       *oid_ptr = 3;
00662       oid_ptr++;
00663     } else if (data < 40) {
00664       *oid_ptr = 0;
00665       oid_ptr++;
00666       *oid_ptr = data;
00667       oid_ptr++;
00668     } else if (data < 80) {
00669       *oid_ptr = 1;
00670       oid_ptr++;
00671       *oid_ptr = data - 40;
00672       oid_ptr++;
00673     } else {
00674       *oid_ptr = 2;
00675       oid_ptr++;
00676       *oid_ptr = data - 80;
00677       oid_ptr++;
00678     }
00679     *oid_len = 2;
00680   } else {
00681     /* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */
00682     return ERR_OK;
00683   }
00684 
00685   while ((len > 0) && (*oid_len < oid_max_len)) {
00686     PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
00687     len--;
00688 
00689     if ((data & 0x80) == 0x00) {
00690       /* sub-identifier uses single octet */
00691       *oid_ptr = data;
00692     } else {
00693       /* sub-identifier uses multiple octets */
00694       u32_t sub_id = (data & ~0x80);
00695       while ((len > 0) && ((data & 0x80) != 0)) {
00696         PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
00697         len--;
00698 
00699         sub_id = (sub_id << 7) + (data & ~0x80);
00700       }
00701 
00702       if ((data & 0x80) != 0) {
00703         /* "more bytes following" bit still set at end of len */
00704         return ERR_VAL;
00705       }
00706       *oid_ptr = sub_id;
00707     }
00708     oid_ptr++;
00709     (*oid_len)++;
00710   }
00711 
00712   if (len > 0) {
00713     /* OID to long to fit in our buffer */
00714     return ERR_MEM;
00715   }
00716 
00717   return ERR_OK;
00718 }
00719 
00720 /**
00721  * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
00722  * from incoming message into array.
00723  *
00724  * @param pbuf_stream points to a pbuf stream
00725  * @param len length of the coded raw data (zero is valid, e.g. empty string!)
00726  * @param buf return raw bytes
00727  * @param buf_len returns length of the raw return value
00728  * @param buf_max_len buffer size
00729  * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
00730  */
00731 err_t
00732 snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t* buf_len, u16_t buf_max_len)
00733 {
00734   if (len > buf_max_len) {
00735     /* not enough dst space */
00736     return ERR_MEM;
00737   }
00738   *buf_len = len;
00739 
00740   while (len > 0) {
00741     PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf));
00742     buf++;
00743     len--;
00744   }
00745 
00746   return ERR_OK;
00747 }
00748 
00749 #endif /* LWIP_SNMP */