1195972f6Sopenharmony_ci/** 2195972f6Sopenharmony_ci * @file 3195972f6Sopenharmony_ci * Abstract Syntax Notation One (ISO 8824, 8825) encoding 4195972f6Sopenharmony_ci * 5195972f6Sopenharmony_ci * @todo not optimised (yet), favor correctness over speed, favor speed over size 6195972f6Sopenharmony_ci */ 7195972f6Sopenharmony_ci 8195972f6Sopenharmony_ci/* 9195972f6Sopenharmony_ci * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. 10195972f6Sopenharmony_ci * All rights reserved. 11195972f6Sopenharmony_ci * 12195972f6Sopenharmony_ci * Redistribution and use in source and binary forms, with or without modification, 13195972f6Sopenharmony_ci * are permitted provided that the following conditions are met: 14195972f6Sopenharmony_ci * 15195972f6Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright notice, 16195972f6Sopenharmony_ci * this list of conditions and the following disclaimer. 17195972f6Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright notice, 18195972f6Sopenharmony_ci * this list of conditions and the following disclaimer in the documentation 19195972f6Sopenharmony_ci * and/or other materials provided with the distribution. 20195972f6Sopenharmony_ci * 3. The name of the author may not be used to endorse or promote products 21195972f6Sopenharmony_ci * derived from this software without specific prior written permission. 22195972f6Sopenharmony_ci * 23195972f6Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 24195972f6Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 25195972f6Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 26195972f6Sopenharmony_ci * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 27195972f6Sopenharmony_ci * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 28195972f6Sopenharmony_ci * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29195972f6Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30195972f6Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 31195972f6Sopenharmony_ci * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 32195972f6Sopenharmony_ci * OF SUCH DAMAGE. 33195972f6Sopenharmony_ci * 34195972f6Sopenharmony_ci * Author: Christiaan Simons <christiaan.simons@axon.tv> 35195972f6Sopenharmony_ci * Martin Hentschel <info@cl-soft.de> 36195972f6Sopenharmony_ci */ 37195972f6Sopenharmony_ci 38195972f6Sopenharmony_ci#include "lwip/apps/snmp_opts.h" 39195972f6Sopenharmony_ci 40195972f6Sopenharmony_ci#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ 41195972f6Sopenharmony_ci 42195972f6Sopenharmony_ci#include "snmp_asn1.h" 43195972f6Sopenharmony_ci 44195972f6Sopenharmony_ci#define PBUF_OP_EXEC(code) \ 45195972f6Sopenharmony_ci if ((code) != ERR_OK) { \ 46195972f6Sopenharmony_ci return ERR_BUF; \ 47195972f6Sopenharmony_ci } 48195972f6Sopenharmony_ci 49195972f6Sopenharmony_ci/** 50195972f6Sopenharmony_ci * Encodes a TLV into a pbuf stream. 51195972f6Sopenharmony_ci * 52195972f6Sopenharmony_ci * @param pbuf_stream points to a pbuf stream 53195972f6Sopenharmony_ci * @param tlv TLV to encode 54195972f6Sopenharmony_ci * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode 55195972f6Sopenharmony_ci */ 56195972f6Sopenharmony_cierr_t 57195972f6Sopenharmony_cisnmp_ans1_enc_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv) 58195972f6Sopenharmony_ci{ 59195972f6Sopenharmony_ci u8_t data; 60195972f6Sopenharmony_ci u8_t length_bytes_required; 61195972f6Sopenharmony_ci 62195972f6Sopenharmony_ci /* write type */ 63195972f6Sopenharmony_ci if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) { 64195972f6Sopenharmony_ci /* extended format is not used by SNMP so we do not accept those values */ 65195972f6Sopenharmony_ci return ERR_ARG; 66195972f6Sopenharmony_ci } 67195972f6Sopenharmony_ci if (tlv->type_len != 0) { 68195972f6Sopenharmony_ci /* any other value as auto is not accepted for type (we always use one byte because extended syntax is prohibited) */ 69195972f6Sopenharmony_ci return ERR_ARG; 70195972f6Sopenharmony_ci } 71195972f6Sopenharmony_ci 72195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, tlv->type)); 73195972f6Sopenharmony_ci tlv->type_len = 1; 74195972f6Sopenharmony_ci 75195972f6Sopenharmony_ci /* write length */ 76195972f6Sopenharmony_ci if (tlv->value_len <= 127) { 77195972f6Sopenharmony_ci length_bytes_required = 1; 78195972f6Sopenharmony_ci } else if (tlv->value_len <= 255) { 79195972f6Sopenharmony_ci length_bytes_required = 2; 80195972f6Sopenharmony_ci } else { 81195972f6Sopenharmony_ci length_bytes_required = 3; 82195972f6Sopenharmony_ci } 83195972f6Sopenharmony_ci 84195972f6Sopenharmony_ci /* check for forced min length */ 85195972f6Sopenharmony_ci if (tlv->length_len > 0) { 86195972f6Sopenharmony_ci if (tlv->length_len < length_bytes_required) { 87195972f6Sopenharmony_ci /* unable to code requested length in requested number of bytes */ 88195972f6Sopenharmony_ci return ERR_ARG; 89195972f6Sopenharmony_ci } 90195972f6Sopenharmony_ci 91195972f6Sopenharmony_ci length_bytes_required = tlv->length_len; 92195972f6Sopenharmony_ci } else { 93195972f6Sopenharmony_ci tlv->length_len = length_bytes_required; 94195972f6Sopenharmony_ci } 95195972f6Sopenharmony_ci 96195972f6Sopenharmony_ci if (length_bytes_required > 1) { 97195972f6Sopenharmony_ci /* multi byte representation required */ 98195972f6Sopenharmony_ci length_bytes_required--; 99195972f6Sopenharmony_ci data = 0x80 | length_bytes_required; /* extended length definition, 1 length byte follows */ 100195972f6Sopenharmony_ci 101195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data)); 102195972f6Sopenharmony_ci 103195972f6Sopenharmony_ci while (length_bytes_required > 1) { 104195972f6Sopenharmony_ci if (length_bytes_required == 2) { 105195972f6Sopenharmony_ci /* append high byte */ 106195972f6Sopenharmony_ci data = (u8_t)(tlv->value_len >> 8); 107195972f6Sopenharmony_ci } else { 108195972f6Sopenharmony_ci /* append leading 0x00 */ 109195972f6Sopenharmony_ci data = 0x00; 110195972f6Sopenharmony_ci } 111195972f6Sopenharmony_ci 112195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data)); 113195972f6Sopenharmony_ci length_bytes_required--; 114195972f6Sopenharmony_ci } 115195972f6Sopenharmony_ci } 116195972f6Sopenharmony_ci 117195972f6Sopenharmony_ci /* append low byte */ 118195972f6Sopenharmony_ci data = (u8_t)(tlv->value_len & 0xFF); 119195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data)); 120195972f6Sopenharmony_ci 121195972f6Sopenharmony_ci return ERR_OK; 122195972f6Sopenharmony_ci} 123195972f6Sopenharmony_ci 124195972f6Sopenharmony_ci/** 125195972f6Sopenharmony_ci * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg. 126195972f6Sopenharmony_ci * 127195972f6Sopenharmony_ci * @param pbuf_stream points to a pbuf stream 128195972f6Sopenharmony_ci * @param raw_len raw data length 129195972f6Sopenharmony_ci * @param raw points raw data 130195972f6Sopenharmony_ci * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode 131195972f6Sopenharmony_ci */ 132195972f6Sopenharmony_cierr_t 133195972f6Sopenharmony_cisnmp_asn1_enc_raw(struct snmp_pbuf_stream *pbuf_stream, const u8_t *raw, u16_t raw_len) 134195972f6Sopenharmony_ci{ 135195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_writebuf(pbuf_stream, raw, raw_len)); 136195972f6Sopenharmony_ci 137195972f6Sopenharmony_ci return ERR_OK; 138195972f6Sopenharmony_ci} 139195972f6Sopenharmony_ci 140195972f6Sopenharmony_ci/** 141195972f6Sopenharmony_ci * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg. 142195972f6Sopenharmony_ci * 143195972f6Sopenharmony_ci * @param pbuf_stream points to a pbuf stream 144195972f6Sopenharmony_ci * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt()) 145195972f6Sopenharmony_ci * @param value is the host order u32_t value to be encoded 146195972f6Sopenharmony_ci * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode 147195972f6Sopenharmony_ci * 148195972f6Sopenharmony_ci * @see snmp_asn1_enc_u32t_cnt() 149195972f6Sopenharmony_ci */ 150195972f6Sopenharmony_cierr_t 151195972f6Sopenharmony_cisnmp_asn1_enc_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u32_t value) 152195972f6Sopenharmony_ci{ 153195972f6Sopenharmony_ci if (octets_needed > 5) { 154195972f6Sopenharmony_ci return ERR_ARG; 155195972f6Sopenharmony_ci } 156195972f6Sopenharmony_ci if (octets_needed == 5) { 157195972f6Sopenharmony_ci /* not enough bits in 'value' add leading 0x00 */ 158195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00)); 159195972f6Sopenharmony_ci octets_needed--; 160195972f6Sopenharmony_ci } 161195972f6Sopenharmony_ci 162195972f6Sopenharmony_ci while (octets_needed > 1) { 163195972f6Sopenharmony_ci octets_needed--; 164195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3)))); 165195972f6Sopenharmony_ci } 166195972f6Sopenharmony_ci 167195972f6Sopenharmony_ci /* (only) one least significant octet */ 168195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value)); 169195972f6Sopenharmony_ci 170195972f6Sopenharmony_ci return ERR_OK; 171195972f6Sopenharmony_ci} 172195972f6Sopenharmony_ci/** 173195972f6Sopenharmony_ci * Encodes s32_t integer into a pbuf chained ASN1 msg. 174195972f6Sopenharmony_ci * 175195972f6Sopenharmony_ci * @param pbuf_stream points to a pbuf stream 176195972f6Sopenharmony_ci * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt()) 177195972f6Sopenharmony_ci * @param value is the host order s32_t value to be encoded 178195972f6Sopenharmony_ci * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode 179195972f6Sopenharmony_ci * 180195972f6Sopenharmony_ci * @see snmp_asn1_enc_s32t_cnt() 181195972f6Sopenharmony_ci */ 182195972f6Sopenharmony_cierr_t 183195972f6Sopenharmony_cisnmp_asn1_enc_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, s32_t value) 184195972f6Sopenharmony_ci{ 185195972f6Sopenharmony_ci while (octets_needed > 1) { 186195972f6Sopenharmony_ci octets_needed--; 187195972f6Sopenharmony_ci 188195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3)))); 189195972f6Sopenharmony_ci } 190195972f6Sopenharmony_ci 191195972f6Sopenharmony_ci /* (only) one least significant octet */ 192195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value)); 193195972f6Sopenharmony_ci 194195972f6Sopenharmony_ci return ERR_OK; 195195972f6Sopenharmony_ci} 196195972f6Sopenharmony_ci 197195972f6Sopenharmony_ci/** 198195972f6Sopenharmony_ci * Encodes object identifier into a pbuf chained ASN1 msg. 199195972f6Sopenharmony_ci * 200195972f6Sopenharmony_ci * @param pbuf_stream points to a pbuf stream 201195972f6Sopenharmony_ci * @param oid points to object identifier array 202195972f6Sopenharmony_ci * @param oid_len object identifier array length 203195972f6Sopenharmony_ci * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode 204195972f6Sopenharmony_ci */ 205195972f6Sopenharmony_cierr_t 206195972f6Sopenharmony_cisnmp_asn1_enc_oid(struct snmp_pbuf_stream *pbuf_stream, const u32_t *oid, u16_t oid_len) 207195972f6Sopenharmony_ci{ 208195972f6Sopenharmony_ci if (oid_len > 1) { 209195972f6Sopenharmony_ci /* write compressed first two sub id's */ 210195972f6Sopenharmony_ci u32_t compressed_byte = ((oid[0] * 40) + oid[1]); 211195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)compressed_byte)); 212195972f6Sopenharmony_ci oid_len -= 2; 213195972f6Sopenharmony_ci oid += 2; 214195972f6Sopenharmony_ci } else { 215195972f6Sopenharmony_ci /* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */ 216195972f6Sopenharmony_ci /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */ 217195972f6Sopenharmony_ci return ERR_ARG; 218195972f6Sopenharmony_ci } 219195972f6Sopenharmony_ci 220195972f6Sopenharmony_ci while (oid_len > 0) { 221195972f6Sopenharmony_ci u32_t sub_id; 222195972f6Sopenharmony_ci u8_t shift, tail; 223195972f6Sopenharmony_ci 224195972f6Sopenharmony_ci oid_len--; 225195972f6Sopenharmony_ci sub_id = *oid; 226195972f6Sopenharmony_ci tail = 0; 227195972f6Sopenharmony_ci shift = 28; 228195972f6Sopenharmony_ci while (shift > 0) { 229195972f6Sopenharmony_ci u8_t code; 230195972f6Sopenharmony_ci 231195972f6Sopenharmony_ci code = (u8_t)(sub_id >> shift); 232195972f6Sopenharmony_ci if ((code != 0) || (tail != 0)) { 233195972f6Sopenharmony_ci tail = 1; 234195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80)); 235195972f6Sopenharmony_ci } 236195972f6Sopenharmony_ci shift -= 7; 237195972f6Sopenharmony_ci } 238195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F)); 239195972f6Sopenharmony_ci 240195972f6Sopenharmony_ci /* proceed to next sub-identifier */ 241195972f6Sopenharmony_ci oid++; 242195972f6Sopenharmony_ci } 243195972f6Sopenharmony_ci return ERR_OK; 244195972f6Sopenharmony_ci} 245195972f6Sopenharmony_ci 246195972f6Sopenharmony_ci/** 247195972f6Sopenharmony_ci * Returns octet count for length. 248195972f6Sopenharmony_ci * 249195972f6Sopenharmony_ci * @param length parameter length 250195972f6Sopenharmony_ci * @param octets_needed points to the return value 251195972f6Sopenharmony_ci */ 252195972f6Sopenharmony_civoid 253195972f6Sopenharmony_cisnmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed) 254195972f6Sopenharmony_ci{ 255195972f6Sopenharmony_ci if (length < 0x80U) { 256195972f6Sopenharmony_ci *octets_needed = 1; 257195972f6Sopenharmony_ci } else if (length < 0x100U) { 258195972f6Sopenharmony_ci *octets_needed = 2; 259195972f6Sopenharmony_ci } else { 260195972f6Sopenharmony_ci *octets_needed = 3; 261195972f6Sopenharmony_ci } 262195972f6Sopenharmony_ci} 263195972f6Sopenharmony_ci 264195972f6Sopenharmony_ci/** 265195972f6Sopenharmony_ci * Returns octet count for an u32_t. 266195972f6Sopenharmony_ci * 267195972f6Sopenharmony_ci * @param value value to be encoded 268195972f6Sopenharmony_ci * @param octets_needed points to the return value 269195972f6Sopenharmony_ci * 270195972f6Sopenharmony_ci * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded 271195972f6Sopenharmony_ci * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value 272195972f6Sopenharmony_ci * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! 273195972f6Sopenharmony_ci */ 274195972f6Sopenharmony_civoid 275195972f6Sopenharmony_cisnmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed) 276195972f6Sopenharmony_ci{ 277195972f6Sopenharmony_ci if (value < 0x80UL) { 278195972f6Sopenharmony_ci *octets_needed = 1; 279195972f6Sopenharmony_ci } else if (value < 0x8000UL) { 280195972f6Sopenharmony_ci *octets_needed = 2; 281195972f6Sopenharmony_ci } else if (value < 0x800000UL) { 282195972f6Sopenharmony_ci *octets_needed = 3; 283195972f6Sopenharmony_ci } else if (value < 0x80000000UL) { 284195972f6Sopenharmony_ci *octets_needed = 4; 285195972f6Sopenharmony_ci } else { 286195972f6Sopenharmony_ci *octets_needed = 5; 287195972f6Sopenharmony_ci } 288195972f6Sopenharmony_ci} 289195972f6Sopenharmony_ci 290195972f6Sopenharmony_ci/** 291195972f6Sopenharmony_ci * Returns octet count for an s32_t. 292195972f6Sopenharmony_ci * 293195972f6Sopenharmony_ci * @param value value to be encoded 294195972f6Sopenharmony_ci * @param octets_needed points to the return value 295195972f6Sopenharmony_ci * 296195972f6Sopenharmony_ci * @note ASN coded integers are _always_ signed. 297195972f6Sopenharmony_ci */ 298195972f6Sopenharmony_civoid 299195972f6Sopenharmony_cisnmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed) 300195972f6Sopenharmony_ci{ 301195972f6Sopenharmony_ci if (value < 0) { 302195972f6Sopenharmony_ci value = ~value; 303195972f6Sopenharmony_ci } 304195972f6Sopenharmony_ci if (value < 0x80L) { 305195972f6Sopenharmony_ci *octets_needed = 1; 306195972f6Sopenharmony_ci } else if (value < 0x8000L) { 307195972f6Sopenharmony_ci *octets_needed = 2; 308195972f6Sopenharmony_ci } else if (value < 0x800000L) { 309195972f6Sopenharmony_ci *octets_needed = 3; 310195972f6Sopenharmony_ci } else { 311195972f6Sopenharmony_ci *octets_needed = 4; 312195972f6Sopenharmony_ci } 313195972f6Sopenharmony_ci} 314195972f6Sopenharmony_ci 315195972f6Sopenharmony_ci/** 316195972f6Sopenharmony_ci * Returns octet count for an object identifier. 317195972f6Sopenharmony_ci * 318195972f6Sopenharmony_ci * @param oid points to object identifier array 319195972f6Sopenharmony_ci * @param oid_len object identifier array length 320195972f6Sopenharmony_ci * @param octets_needed points to the return value 321195972f6Sopenharmony_ci */ 322195972f6Sopenharmony_civoid 323195972f6Sopenharmony_cisnmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed) 324195972f6Sopenharmony_ci{ 325195972f6Sopenharmony_ci u32_t sub_id; 326195972f6Sopenharmony_ci 327195972f6Sopenharmony_ci *octets_needed = 0; 328195972f6Sopenharmony_ci if (oid_len > 1) { 329195972f6Sopenharmony_ci /* compressed prefix in one octet */ 330195972f6Sopenharmony_ci (*octets_needed)++; 331195972f6Sopenharmony_ci oid_len -= 2; 332195972f6Sopenharmony_ci oid += 2; 333195972f6Sopenharmony_ci } 334195972f6Sopenharmony_ci while (oid_len > 0) { 335195972f6Sopenharmony_ci oid_len--; 336195972f6Sopenharmony_ci sub_id = *oid; 337195972f6Sopenharmony_ci 338195972f6Sopenharmony_ci sub_id >>= 7; 339195972f6Sopenharmony_ci (*octets_needed)++; 340195972f6Sopenharmony_ci while (sub_id > 0) { 341195972f6Sopenharmony_ci sub_id >>= 7; 342195972f6Sopenharmony_ci (*octets_needed)++; 343195972f6Sopenharmony_ci } 344195972f6Sopenharmony_ci oid++; 345195972f6Sopenharmony_ci } 346195972f6Sopenharmony_ci} 347195972f6Sopenharmony_ci 348195972f6Sopenharmony_ci/** 349195972f6Sopenharmony_ci * Decodes a TLV from a pbuf stream. 350195972f6Sopenharmony_ci * 351195972f6Sopenharmony_ci * @param pbuf_stream points to a pbuf stream 352195972f6Sopenharmony_ci * @param tlv returns decoded TLV 353195972f6Sopenharmony_ci * @return ERR_OK if successful, ERR_VAL if we can't decode 354195972f6Sopenharmony_ci */ 355195972f6Sopenharmony_cierr_t 356195972f6Sopenharmony_cisnmp_asn1_dec_tlv(struct snmp_pbuf_stream *pbuf_stream, struct snmp_asn1_tlv *tlv) 357195972f6Sopenharmony_ci{ 358195972f6Sopenharmony_ci u8_t data; 359195972f6Sopenharmony_ci 360195972f6Sopenharmony_ci /* decode type first */ 361195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 362195972f6Sopenharmony_ci tlv->type = data; 363195972f6Sopenharmony_ci 364195972f6Sopenharmony_ci if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) { 365195972f6Sopenharmony_ci /* extended format is not used by SNMP so we do not accept those values */ 366195972f6Sopenharmony_ci return ERR_VAL; 367195972f6Sopenharmony_ci } 368195972f6Sopenharmony_ci tlv->type_len = 1; 369195972f6Sopenharmony_ci 370195972f6Sopenharmony_ci /* now, decode length */ 371195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 372195972f6Sopenharmony_ci 373195972f6Sopenharmony_ci if (data < 0x80) { /* short form */ 374195972f6Sopenharmony_ci tlv->length_len = 1; 375195972f6Sopenharmony_ci tlv->value_len = data; 376195972f6Sopenharmony_ci } else if (data > 0x80) { /* long form */ 377195972f6Sopenharmony_ci u8_t length_bytes = data - 0x80; 378195972f6Sopenharmony_ci if (length_bytes > pbuf_stream->length) { 379195972f6Sopenharmony_ci return ERR_VAL; 380195972f6Sopenharmony_ci } 381195972f6Sopenharmony_ci tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */ 382195972f6Sopenharmony_ci tlv->value_len = 0; 383195972f6Sopenharmony_ci 384195972f6Sopenharmony_ci while (length_bytes > 0) { 385195972f6Sopenharmony_ci /* we only support up to u16.maxvalue-1 (2 bytes) but have to accept leading zero bytes */ 386195972f6Sopenharmony_ci if (tlv->value_len > 0xFF) { 387195972f6Sopenharmony_ci return ERR_VAL; 388195972f6Sopenharmony_ci } 389195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 390195972f6Sopenharmony_ci tlv->value_len <<= 8; 391195972f6Sopenharmony_ci tlv->value_len |= data; 392195972f6Sopenharmony_ci 393195972f6Sopenharmony_ci /* take care for special value used for indefinite length */ 394195972f6Sopenharmony_ci if (tlv->value_len == 0xFFFF) { 395195972f6Sopenharmony_ci return ERR_VAL; 396195972f6Sopenharmony_ci } 397195972f6Sopenharmony_ci 398195972f6Sopenharmony_ci length_bytes--; 399195972f6Sopenharmony_ci } 400195972f6Sopenharmony_ci } else { /* data == 0x80 indefinite length form */ 401195972f6Sopenharmony_ci /* (not allowed for SNMP; RFC 1157, 3.2.2) */ 402195972f6Sopenharmony_ci return ERR_VAL; 403195972f6Sopenharmony_ci } 404195972f6Sopenharmony_ci 405195972f6Sopenharmony_ci return ERR_OK; 406195972f6Sopenharmony_ci} 407195972f6Sopenharmony_ci 408195972f6Sopenharmony_ci/** 409195972f6Sopenharmony_ci * Decodes positive integer (counter, gauge, timeticks) into u32_t. 410195972f6Sopenharmony_ci * 411195972f6Sopenharmony_ci * @param pbuf_stream points to a pbuf stream 412195972f6Sopenharmony_ci * @param len length of the coded integer field 413195972f6Sopenharmony_ci * @param value return host order integer 414195972f6Sopenharmony_ci * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode 415195972f6Sopenharmony_ci * 416195972f6Sopenharmony_ci * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded 417195972f6Sopenharmony_ci * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value 418195972f6Sopenharmony_ci * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! 419195972f6Sopenharmony_ci */ 420195972f6Sopenharmony_cierr_t 421195972f6Sopenharmony_cisnmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value) 422195972f6Sopenharmony_ci{ 423195972f6Sopenharmony_ci u8_t data; 424195972f6Sopenharmony_ci 425195972f6Sopenharmony_ci if ((len > 0) && (len <= 5)) { 426195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 427195972f6Sopenharmony_ci 428195972f6Sopenharmony_ci /* expecting sign bit to be zero, only unsigned please! */ 429195972f6Sopenharmony_ci if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) { 430195972f6Sopenharmony_ci *value = data; 431195972f6Sopenharmony_ci len--; 432195972f6Sopenharmony_ci 433195972f6Sopenharmony_ci while (len > 0) { 434195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 435195972f6Sopenharmony_ci len--; 436195972f6Sopenharmony_ci 437195972f6Sopenharmony_ci *value <<= 8; 438195972f6Sopenharmony_ci *value |= data; 439195972f6Sopenharmony_ci } 440195972f6Sopenharmony_ci 441195972f6Sopenharmony_ci return ERR_OK; 442195972f6Sopenharmony_ci } 443195972f6Sopenharmony_ci } 444195972f6Sopenharmony_ci 445195972f6Sopenharmony_ci return ERR_VAL; 446195972f6Sopenharmony_ci} 447195972f6Sopenharmony_ci 448195972f6Sopenharmony_ci/** 449195972f6Sopenharmony_ci * Decodes integer into s32_t. 450195972f6Sopenharmony_ci * 451195972f6Sopenharmony_ci * @param pbuf_stream points to a pbuf stream 452195972f6Sopenharmony_ci * @param len length of the coded integer field 453195972f6Sopenharmony_ci * @param value return host order integer 454195972f6Sopenharmony_ci * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode 455195972f6Sopenharmony_ci * 456195972f6Sopenharmony_ci * @note ASN coded integers are _always_ signed! 457195972f6Sopenharmony_ci */ 458195972f6Sopenharmony_cierr_t 459195972f6Sopenharmony_cisnmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value) 460195972f6Sopenharmony_ci{ 461195972f6Sopenharmony_ci u8_t data; 462195972f6Sopenharmony_ci 463195972f6Sopenharmony_ci if ((len > 0) && (len < 5)) { 464195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 465195972f6Sopenharmony_ci 466195972f6Sopenharmony_ci if (data & 0x80) { 467195972f6Sopenharmony_ci /* negative, start from -1 */ 468195972f6Sopenharmony_ci *value = -1; 469195972f6Sopenharmony_ci *value = (*value << 8) | data; 470195972f6Sopenharmony_ci } else { 471195972f6Sopenharmony_ci /* positive, start from 0 */ 472195972f6Sopenharmony_ci *value = data; 473195972f6Sopenharmony_ci } 474195972f6Sopenharmony_ci len--; 475195972f6Sopenharmony_ci /* shift in the remaining value */ 476195972f6Sopenharmony_ci while (len > 0) { 477195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 478195972f6Sopenharmony_ci *value = (*value << 8) | data; 479195972f6Sopenharmony_ci len--; 480195972f6Sopenharmony_ci } 481195972f6Sopenharmony_ci return ERR_OK; 482195972f6Sopenharmony_ci } 483195972f6Sopenharmony_ci 484195972f6Sopenharmony_ci return ERR_VAL; 485195972f6Sopenharmony_ci} 486195972f6Sopenharmony_ci 487195972f6Sopenharmony_ci/** 488195972f6Sopenharmony_ci * Decodes object identifier from incoming message into array of u32_t. 489195972f6Sopenharmony_ci * 490195972f6Sopenharmony_ci * @param pbuf_stream points to a pbuf stream 491195972f6Sopenharmony_ci * @param len length of the coded object identifier 492195972f6Sopenharmony_ci * @param oid return decoded object identifier 493195972f6Sopenharmony_ci * @param oid_len return decoded object identifier length 494195972f6Sopenharmony_ci * @param oid_max_len size of oid buffer 495195972f6Sopenharmony_ci * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode 496195972f6Sopenharmony_ci */ 497195972f6Sopenharmony_cierr_t 498195972f6Sopenharmony_cisnmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *oid, u8_t *oid_len, u8_t oid_max_len) 499195972f6Sopenharmony_ci{ 500195972f6Sopenharmony_ci u32_t *oid_ptr; 501195972f6Sopenharmony_ci u8_t data; 502195972f6Sopenharmony_ci 503195972f6Sopenharmony_ci *oid_len = 0; 504195972f6Sopenharmony_ci oid_ptr = oid; 505195972f6Sopenharmony_ci if (len > 0) { 506195972f6Sopenharmony_ci if (oid_max_len < 2) { 507195972f6Sopenharmony_ci return ERR_MEM; 508195972f6Sopenharmony_ci } 509195972f6Sopenharmony_ci 510195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 511195972f6Sopenharmony_ci len--; 512195972f6Sopenharmony_ci 513195972f6Sopenharmony_ci /* first compressed octet */ 514195972f6Sopenharmony_ci if (data == 0x2B) { 515195972f6Sopenharmony_ci /* (most) common case 1.3 (iso.org) */ 516195972f6Sopenharmony_ci *oid_ptr = 1; 517195972f6Sopenharmony_ci oid_ptr++; 518195972f6Sopenharmony_ci *oid_ptr = 3; 519195972f6Sopenharmony_ci oid_ptr++; 520195972f6Sopenharmony_ci } else if (data < 40) { 521195972f6Sopenharmony_ci *oid_ptr = 0; 522195972f6Sopenharmony_ci oid_ptr++; 523195972f6Sopenharmony_ci *oid_ptr = data; 524195972f6Sopenharmony_ci oid_ptr++; 525195972f6Sopenharmony_ci } else if (data < 80) { 526195972f6Sopenharmony_ci *oid_ptr = 1; 527195972f6Sopenharmony_ci oid_ptr++; 528195972f6Sopenharmony_ci *oid_ptr = data - 40; 529195972f6Sopenharmony_ci oid_ptr++; 530195972f6Sopenharmony_ci } else { 531195972f6Sopenharmony_ci *oid_ptr = 2; 532195972f6Sopenharmony_ci oid_ptr++; 533195972f6Sopenharmony_ci *oid_ptr = data - 80; 534195972f6Sopenharmony_ci oid_ptr++; 535195972f6Sopenharmony_ci } 536195972f6Sopenharmony_ci *oid_len = 2; 537195972f6Sopenharmony_ci } else { 538195972f6Sopenharmony_ci /* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */ 539195972f6Sopenharmony_ci return ERR_OK; 540195972f6Sopenharmony_ci } 541195972f6Sopenharmony_ci 542195972f6Sopenharmony_ci while ((len > 0) && (*oid_len < oid_max_len)) { 543195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 544195972f6Sopenharmony_ci len--; 545195972f6Sopenharmony_ci 546195972f6Sopenharmony_ci if ((data & 0x80) == 0x00) { 547195972f6Sopenharmony_ci /* sub-identifier uses single octet */ 548195972f6Sopenharmony_ci *oid_ptr = data; 549195972f6Sopenharmony_ci } else { 550195972f6Sopenharmony_ci /* sub-identifier uses multiple octets */ 551195972f6Sopenharmony_ci u32_t sub_id = (data & ~0x80); 552195972f6Sopenharmony_ci while ((len > 0) && ((data & 0x80) != 0)) { 553195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 554195972f6Sopenharmony_ci len--; 555195972f6Sopenharmony_ci 556195972f6Sopenharmony_ci sub_id = (sub_id << 7) + (data & ~0x80); 557195972f6Sopenharmony_ci } 558195972f6Sopenharmony_ci 559195972f6Sopenharmony_ci if ((data & 0x80) != 0) { 560195972f6Sopenharmony_ci /* "more bytes following" bit still set at end of len */ 561195972f6Sopenharmony_ci return ERR_VAL; 562195972f6Sopenharmony_ci } 563195972f6Sopenharmony_ci *oid_ptr = sub_id; 564195972f6Sopenharmony_ci } 565195972f6Sopenharmony_ci oid_ptr++; 566195972f6Sopenharmony_ci (*oid_len)++; 567195972f6Sopenharmony_ci } 568195972f6Sopenharmony_ci 569195972f6Sopenharmony_ci if (len > 0) { 570195972f6Sopenharmony_ci /* OID to long to fit in our buffer */ 571195972f6Sopenharmony_ci return ERR_MEM; 572195972f6Sopenharmony_ci } 573195972f6Sopenharmony_ci 574195972f6Sopenharmony_ci return ERR_OK; 575195972f6Sopenharmony_ci} 576195972f6Sopenharmony_ci 577195972f6Sopenharmony_ci/** 578195972f6Sopenharmony_ci * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding) 579195972f6Sopenharmony_ci * from incoming message into array. 580195972f6Sopenharmony_ci * 581195972f6Sopenharmony_ci * @param pbuf_stream points to a pbuf stream 582195972f6Sopenharmony_ci * @param len length of the coded raw data (zero is valid, e.g. empty string!) 583195972f6Sopenharmony_ci * @param buf return raw bytes 584195972f6Sopenharmony_ci * @param buf_len returns length of the raw return value 585195972f6Sopenharmony_ci * @param buf_max_len buffer size 586195972f6Sopenharmony_ci * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode 587195972f6Sopenharmony_ci */ 588195972f6Sopenharmony_cierr_t 589195972f6Sopenharmony_cisnmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t *buf_len, u16_t buf_max_len) 590195972f6Sopenharmony_ci{ 591195972f6Sopenharmony_ci if (len > buf_max_len) { 592195972f6Sopenharmony_ci /* not enough dst space */ 593195972f6Sopenharmony_ci return ERR_MEM; 594195972f6Sopenharmony_ci } 595195972f6Sopenharmony_ci *buf_len = len; 596195972f6Sopenharmony_ci 597195972f6Sopenharmony_ci while (len > 0) { 598195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf)); 599195972f6Sopenharmony_ci buf++; 600195972f6Sopenharmony_ci len--; 601195972f6Sopenharmony_ci } 602195972f6Sopenharmony_ci 603195972f6Sopenharmony_ci return ERR_OK; 604195972f6Sopenharmony_ci} 605195972f6Sopenharmony_ci 606195972f6Sopenharmony_ci#if LWIP_HAVE_INT64 607195972f6Sopenharmony_ci/** 608195972f6Sopenharmony_ci * Returns octet count for an u64_t. 609195972f6Sopenharmony_ci * 610195972f6Sopenharmony_ci * @param value value to be encoded 611195972f6Sopenharmony_ci * @param octets_needed points to the return value 612195972f6Sopenharmony_ci * 613195972f6Sopenharmony_ci * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded 614195972f6Sopenharmony_ci * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value 615195972f6Sopenharmony_ci * of 0xFFFFFFFFFFFFFFFF is preceded with 0x00 and the length is 9 octets!! 616195972f6Sopenharmony_ci */ 617195972f6Sopenharmony_civoid 618195972f6Sopenharmony_cisnmp_asn1_enc_u64t_cnt(u64_t value, u16_t *octets_needed) 619195972f6Sopenharmony_ci{ 620195972f6Sopenharmony_ci /* check if high u32 is 0 */ 621195972f6Sopenharmony_ci if ((value >> 32) == 0) { 622195972f6Sopenharmony_ci /* only low u32 is important */ 623195972f6Sopenharmony_ci snmp_asn1_enc_u32t_cnt((u32_t)value, octets_needed); 624195972f6Sopenharmony_ci } else { 625195972f6Sopenharmony_ci /* low u32 does not matter for length determination */ 626195972f6Sopenharmony_ci snmp_asn1_enc_u32t_cnt((u32_t)(value >> 32), octets_needed); 627195972f6Sopenharmony_ci *octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */ 628195972f6Sopenharmony_ci } 629195972f6Sopenharmony_ci} 630195972f6Sopenharmony_ci 631195972f6Sopenharmony_ci/** 632195972f6Sopenharmony_ci * Decodes large positive integer (counter64) into 2x u32_t. 633195972f6Sopenharmony_ci * 634195972f6Sopenharmony_ci * @param pbuf_stream points to a pbuf stream 635195972f6Sopenharmony_ci * @param len length of the coded integer field 636195972f6Sopenharmony_ci * @param value return 64 bit integer 637195972f6Sopenharmony_ci * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode 638195972f6Sopenharmony_ci * 639195972f6Sopenharmony_ci * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded 640195972f6Sopenharmony_ci * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value 641195972f6Sopenharmony_ci * of 0xFFFFFFFFFFFFFFFF is preceded with 0x00 and the length is 9 octets!! 642195972f6Sopenharmony_ci */ 643195972f6Sopenharmony_cierr_t 644195972f6Sopenharmony_cisnmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u64_t *value) 645195972f6Sopenharmony_ci{ 646195972f6Sopenharmony_ci u8_t data; 647195972f6Sopenharmony_ci 648195972f6Sopenharmony_ci if ((len > 0) && (len <= 9)) { 649195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 650195972f6Sopenharmony_ci 651195972f6Sopenharmony_ci /* expecting sign bit to be zero, only unsigned please! */ 652195972f6Sopenharmony_ci if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) { 653195972f6Sopenharmony_ci *value = data; 654195972f6Sopenharmony_ci len--; 655195972f6Sopenharmony_ci 656195972f6Sopenharmony_ci while (len > 0) { 657195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data)); 658195972f6Sopenharmony_ci *value <<= 8; 659195972f6Sopenharmony_ci *value |= data; 660195972f6Sopenharmony_ci len--; 661195972f6Sopenharmony_ci } 662195972f6Sopenharmony_ci 663195972f6Sopenharmony_ci return ERR_OK; 664195972f6Sopenharmony_ci } 665195972f6Sopenharmony_ci } 666195972f6Sopenharmony_ci 667195972f6Sopenharmony_ci return ERR_VAL; 668195972f6Sopenharmony_ci} 669195972f6Sopenharmony_ci 670195972f6Sopenharmony_ci/** 671195972f6Sopenharmony_ci * Encodes u64_t (counter64) into a pbuf chained ASN1 msg. 672195972f6Sopenharmony_ci * 673195972f6Sopenharmony_ci * @param pbuf_stream points to a pbuf stream 674195972f6Sopenharmony_ci * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt()) 675195972f6Sopenharmony_ci * @param value is the value to be encoded 676195972f6Sopenharmony_ci * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode 677195972f6Sopenharmony_ci * 678195972f6Sopenharmony_ci * @see snmp_asn1_enc_u64t_cnt() 679195972f6Sopenharmony_ci */ 680195972f6Sopenharmony_cierr_t 681195972f6Sopenharmony_cisnmp_asn1_enc_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u64_t value) 682195972f6Sopenharmony_ci{ 683195972f6Sopenharmony_ci if (octets_needed > 9) { 684195972f6Sopenharmony_ci return ERR_ARG; 685195972f6Sopenharmony_ci } 686195972f6Sopenharmony_ci if (octets_needed == 9) { 687195972f6Sopenharmony_ci /* not enough bits in 'value' add leading 0x00 */ 688195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00)); 689195972f6Sopenharmony_ci octets_needed--; 690195972f6Sopenharmony_ci } 691195972f6Sopenharmony_ci 692195972f6Sopenharmony_ci while (octets_needed > 1) { 693195972f6Sopenharmony_ci octets_needed--; 694195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3)))); 695195972f6Sopenharmony_ci } 696195972f6Sopenharmony_ci 697195972f6Sopenharmony_ci /* always write at least one octet (also in case of value == 0) */ 698195972f6Sopenharmony_ci PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value))); 699195972f6Sopenharmony_ci 700195972f6Sopenharmony_ci return ERR_OK; 701195972f6Sopenharmony_ci} 702195972f6Sopenharmony_ci#endif 703195972f6Sopenharmony_ci 704195972f6Sopenharmony_ci#endif /* LWIP_SNMP */ 705