/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include "cborattr/cborattr.h" #include "tinycbor/cbor.h" #include "tinycbor/cbor_buf_reader.h" #ifdef __ZEPHYR__ #include #ifdef CONFIG_MGMT_CBORATTR_MAX_SIZE #define CBORATTR_MAX_SIZE CONFIG_MGMT_CBORATTR_MAX_SIZE #else #define CBORATTR_MAX_SIZE 512 #endif #endif #include "syscfg/syscfg.h" #include "tinycbor/cbor_mbuf_reader.h" #include "tinycbor/cbor_mbuf_writer.h" #include "os/os_mbuf.h" #define CBORATTR_MAX_SIZE 512 /* this maps a CborType to a matching CborAtter Type. The mapping is not * one-to-one because of signedness of integers * and therefore we need a function to do this trickery */ static int valid_attr_type(CborType ct, CborAttrType at) { switch (at) { case CborAttrIntegerType: case CborAttrUnsignedIntegerType: if (ct == CborIntegerType) { return 1; } break; case CborAttrByteStringType: if (ct == CborByteStringType) { return 1; } break; case CborAttrTextStringType: if (ct == CborTextStringType) { return 1; } break; case CborAttrBooleanType: if (ct == CborBooleanType) { return 1; } break; #if FLOAT_SUPPORT case CborAttrHalfFloatType: if (ct == CborHalfFloatType) { return 1; } break; case CborAttrFloatType: if (ct == CborFloatType) { return 1; } break; case CborAttrDoubleType: if (ct == CborDoubleType) { return 1; } break; #endif case CborAttrArrayType: if (ct == CborArrayType) { return 1; } break; case CborAttrObjectType: if (ct == CborMapType) { return 1; } break; case CborAttrNullType: if (ct == CborNullType) { return 1; } break; default: break; } return 0; } /* this function find the pointer to the memory location to * write or read and attribute from the cbor_attr_r structure */ static char * cbor_target_address(const struct cbor_attr_t *cursor, const struct cbor_array_t *parent, int offset) { char *targetaddr = NULL; if (parent == NULL || parent->element_type != CborAttrStructObjectType) { /* ordinary case - use the address in the cursor structure */ switch (cursor->type) { case CborAttrNullType: targetaddr = NULL; break; case CborAttrIntegerType: targetaddr = (char *)&cursor->addr.integer[offset]; break; case CborAttrUnsignedIntegerType: targetaddr = (char *)&cursor->addr.uinteger[offset]; break; #if FLOAT_SUPPORT case CborAttrHalfFloatType: targetaddr = (char *)&cursor->addr.halffloat[offset]; break; case CborAttrFloatType: targetaddr = (char *)&cursor->addr.fval[offset]; break; case CborAttrDoubleType: targetaddr = (char *)&cursor->addr.real[offset]; break; #endif case CborAttrByteStringType: targetaddr = (char *) cursor->addr.bytestring.data; break; case CborAttrTextStringType: targetaddr = cursor->addr.string; break; case CborAttrBooleanType: targetaddr = (char *)&cursor->addr.boolean[offset]; break; default: targetaddr = NULL; break; } } else { /* tricky case - hacking a member in an array of structures */ targetaddr = parent->arr.objects.base + (offset * parent->arr.objects.stride) + cursor->addr.offset; } return targetaddr; } static int cbor_internal_read_object(CborValue *root_value, const struct cbor_attr_t *attrs, const struct cbor_array_t *parent, int offset) { const struct cbor_attr_t *cursor, *best_match; char attrbuf[CBORATTR_MAX_SIZE + 1]; void *lptr; CborValue cur_value; CborError err = 0; size_t len = 0; CborType type = CborInvalidType; /* stuff fields with defaults in case they're omitted in the JSON input */ for (cursor = attrs; cursor->attribute != NULL; cursor++) { if (!cursor->nodefault) { lptr = cbor_target_address(cursor, parent, offset); if (lptr != NULL) { switch (cursor->type) { case CborAttrIntegerType: memcpy(lptr, &cursor->dflt.integer, sizeof(long long int)); break; case CborAttrUnsignedIntegerType: memcpy(lptr, &cursor->dflt.integer, sizeof(long long unsigned int)); break; case CborAttrBooleanType: memcpy(lptr, &cursor->dflt.boolean, sizeof(bool)); break; #if FLOAT_SUPPORT case CborAttrHalfFloatType: memcpy(lptr, &cursor->dflt.halffloat, sizeof(uint16_t)); break; case CborAttrFloatType: memcpy(lptr, &cursor->dflt.fval, sizeof(float)); break; case CborAttrDoubleType: memcpy(lptr, &cursor->dflt.real, sizeof(double)); break; #endif default: break; } } } } if (cbor_value_is_map(root_value)) { err |= cbor_value_enter_container(root_value, &cur_value); } else { err |= CborErrorIllegalType; return err; } /* contains key value pairs */ while (cbor_value_is_valid(&cur_value) && !err) { /* get the attribute */ if (cbor_value_is_text_string(&cur_value)) { if (cbor_value_calculate_string_length(&cur_value, &len) == 0) { if (len > CBORATTR_MAX_SIZE) { err |= CborErrorDataTooLarge; break; } err |= cbor_value_copy_text_string(&cur_value, attrbuf, &len, NULL); } /* at least get the type of the next value so we can match the * attribute name and type for a perfect match */ err |= cbor_value_advance(&cur_value); if (cbor_value_is_valid(&cur_value)) { type = cbor_value_get_type(&cur_value); } else { err |= CborErrorIllegalType; break; } } else { attrbuf[0] = '\0'; type = cbor_value_get_type(&cur_value); } /* find this attribute in our list */ best_match = NULL; for (cursor = attrs; cursor->attribute != NULL; cursor++) { if (valid_attr_type(type, cursor->type)) { if (cursor->attribute == CBORATTR_ATTR_UNNAMED && attrbuf[0] == '\0') { best_match = cursor; } else if (strlen(cursor->attribute) == len && !memcmp(cursor->attribute, attrbuf, len)) { break; } } } if (!cursor->attribute && best_match) { cursor = best_match; } /* we found a match */ if (cursor->attribute != NULL) { lptr = cbor_target_address(cursor, parent, offset); switch (cursor->type) { case CborAttrNullType: /* nothing to do */ break; case CborAttrBooleanType: err |= cbor_value_get_boolean(&cur_value, lptr); break; case CborAttrIntegerType: err |= cbor_value_get_int64(&cur_value, lptr); break; case CborAttrUnsignedIntegerType: err |= cbor_value_get_uint64(&cur_value, lptr); break; #if FLOAT_SUPPORT case CborAttrHalfFloatType: err |= cbor_value_get_half_float(&cur_value, lptr); break; case CborAttrFloatType: err |= cbor_value_get_float(&cur_value, lptr); break; case CborAttrDoubleType: err |= cbor_value_get_double(&cur_value, lptr); break; #endif case CborAttrByteStringType: { size_t len = cursor->len; err |= cbor_value_copy_byte_string(&cur_value, lptr, &len, NULL); *cursor->addr.bytestring.len = len; break; } case CborAttrTextStringType: { size_t len = cursor->len; err |= cbor_value_copy_text_string(&cur_value, lptr, &len, NULL); break; } case CborAttrArrayType: err |= cbor_read_array(&cur_value, &cursor->addr.array); continue; case CborAttrObjectType: err |= cbor_internal_read_object(&cur_value, cursor->addr.obj, NULL, 0); continue; default: err |= CborErrorIllegalType; } } err = cbor_value_advance(&cur_value); } if (!err) { /* that should be it for this container */ err |= cbor_value_leave_container(root_value, &cur_value); } return err; } int cbor_read_array(struct CborValue *value, const struct cbor_array_t *arr) { CborError err = 0; struct CborValue elem; int off, arrcount; size_t len; void *lptr; char *tp; err = cbor_value_enter_container(value, &elem); if (err) { return err; } arrcount = 0; tp = arr->arr.strings.store; for (off = 0; off < arr->maxlen; off++) { switch (arr->element_type) { case CborAttrBooleanType: lptr = &arr->arr.booleans.store[off]; err |= cbor_value_get_boolean(&elem, lptr); break; case CborAttrIntegerType: lptr = &arr->arr.integers.store[off]; err |= cbor_value_get_int64(&elem, lptr); break; case CborAttrUnsignedIntegerType: lptr = &arr->arr.uintegers.store[off]; err |= cbor_value_get_uint64(&elem, lptr); break; #if FLOAT_SUPPORT case CborAttrHalfFloatType: lptr = &arr->arr.halffloats.store[off]; err |= cbor_value_get_half_float(&elem, lptr); break; case CborAttrFloatType: case CborAttrDoubleType: lptr = &arr->arr.reals.store[off]; err |= cbor_value_get_double(&elem, lptr); break; #endif case CborAttrTextStringType: len = arr->arr.strings.storelen - (tp - arr->arr.strings.store); err |= cbor_value_copy_text_string(&elem, tp, &len, NULL); arr->arr.strings.ptrs[off] = tp; tp += len + 1; break; case CborAttrStructObjectType: err |= cbor_internal_read_object(&elem, arr->arr.objects.subtype, arr, off); break; default: err |= CborErrorIllegalType; break; } arrcount++; if (arr->element_type != CborAttrStructObjectType) { err |= cbor_value_advance(&elem); } if (!cbor_value_is_valid(&elem)) { break; } } if (arr->count) { *arr->count = arrcount; } while (!cbor_value_at_end(&elem)) { err |= CborErrorDataTooLarge; cbor_value_advance(&elem); } err |= cbor_value_leave_container(value, &elem); return err; } int cbor_read_object(struct CborValue *value, const struct cbor_attr_t *attrs) { int st; st = cbor_internal_read_object(value, attrs, NULL, 0); return st; } /* * Read in cbor key/values from flat buffer pointed by data, and fill them * into attrs. * * @param data Pointer to beginning of cbor encoded data * @param len Number of bytes in the buffer * @param attrs Array of cbor objects to look for. * * @return 0 on success; non-zero on failure. */ int cbor_read_flat_attrs(const uint8_t *data, int len, const struct cbor_attr_t *attrs) { struct cbor_buf_reader reader; struct CborParser parser; struct CborValue value; CborError err; cbor_buf_reader_init(&reader, data, len); err = cbor_parser_init(&reader.r, 0, &parser, &value); if (err != CborNoError) { return -1; } return cbor_read_object(&value, attrs); } #ifdef MYNEWT static int cbor_write_val(struct CborEncoder *enc, const struct cbor_out_val_t *val); /* * Read in cbor key/values from os_mbuf pointed by m, and fill them * into attrs. * * @param m Pointer to os_mbuf containing cbor encoded data * @param off Offset into mbuf where cbor data begins * @param len Number of bytes to decode * @param attrs Array of cbor objects to look for. * * @return 0 on success; non-zero on failure. */ int cbor_read_mbuf_attrs(struct os_mbuf *m, uint16_t off, uint16_t len, const struct cbor_attr_t *attrs) { struct cbor_mbuf_reader cmr; struct CborParser parser; struct CborValue value; CborError err; cbor_mbuf_reader_init(&cmr, m, off); err = cbor_parser_init(&cmr.r, 0, &parser, &value); if (err != CborNoError) { return -1; } return cbor_read_object(&value, attrs); } static int cbor_write_arr_val(struct CborEncoder *enc, const struct cbor_out_arr_val_t *arr) { struct CborEncoder arr_enc; size_t i; int rc; rc = cbor_encoder_create_array(enc, &arr_enc, arr->len); if (rc != 0) { return SYS_ENOMEM; } for (i = 0; i < arr->len; i++) { rc = cbor_write_val(&arr_enc, &arr->elems[i]); if (rc != 0) { return SYS_ENOMEM; } } rc = cbor_encoder_close_container(enc, &arr_enc); if (rc != 0) { return SYS_ENOMEM; } return 0; } static int cbor_write_val(struct CborEncoder *enc, const struct cbor_out_val_t *val) { int len; int rc; switch (val->type) { case CborAttrNullType: rc = cbor_encode_null(enc); break; case CborAttrBooleanType: rc = cbor_encode_boolean(enc, val->boolean); break; case CborAttrIntegerType: rc = cbor_encode_int(enc, val->integer); break; case CborAttrUnsignedIntegerType: rc = cbor_encode_uint(enc, val->uinteger); break; #if FLOAT_SUPPORT case CborAttrHalfFloatType: rc = cbor_encode_half_float(enc, &val->halffloat); break; case CborAttrFloatType: rc = cbor_encode_float(enc, val->fval); break; case CborAttrDoubleType: rc = cbor_encode_double(enc, val->real); break; #endif case CborAttrByteStringType: if (val->bytestring.data == NULL && val->bytestring.len != 0) { return SYS_EINVAL; } rc = cbor_encode_byte_string(enc, val->bytestring.data, val->bytestring.len); break; case CborAttrTextStringType: if (val->string == NULL) { len = 0; } else { len = strlen(val->string); } rc = cbor_encode_text_string(enc, val->string, len); break; case CborAttrObjectType: rc = cbor_write_object(enc, val->obj); break; case CborAttrArrayType: rc = cbor_write_arr_val(enc, &val->array); break; default: return SYS_ENOTSUP; } if (rc != 0) { return SYS_ENOMEM; } return 0; } static int cbor_write_attr(struct CborEncoder *enc, const struct cbor_out_attr_t *attr) { int len; int rc; if (attr->omit) { return 0; } if (!attr->attribute) { rc = SYS_EINVAL; return rc; } len = strlen(attr->attribute); rc = cbor_encode_text_string(enc, attr->attribute, len); if (rc != 0) { return rc; } rc = cbor_write_val(enc, &attr->val); if (rc != 0) { return rc; } return 0; } int cbor_write_object(struct CborEncoder *enc, const struct cbor_out_attr_t *attrs) { const struct cbor_out_attr_t *attr; struct CborEncoder map; int rc; rc = cbor_encoder_create_map(enc, &map, CborIndefiniteLength); if (rc != 0) { return SYS_ENOMEM; } for (attr = attrs; attr->val.type != 0; attr++) { rc = cbor_write_attr(&map, attr); if (rc != 0) { return rc; } } rc = cbor_encoder_close_container(enc, &map); if (rc != 0) { return SYS_ENOMEM; } return 0; } int cbor_write_object_msys(const struct cbor_out_attr_t *attrs, struct os_mbuf **out_om) { struct cbor_mbuf_writer writer; struct CborEncoder encoder; int rc; *out_om = os_msys_get_pkthdr(0, 0); if (*out_om == NULL) { return SYS_ENOMEM; } cbor_mbuf_writer_init(&writer, *out_om); cbor_encoder_init(&encoder, &writer.enc, 0); rc = cbor_write_object(&encoder, attrs); if (rc != 0) { os_mbuf_free_chain(*out_om); *out_om = NULL; return rc; } return 0; } #endif