|  | /* SDPX-License-Identifier: MIT */ | 
|  |  | 
|  | #ifdef __cplusplus | 
|  | extern "C" { | 
|  | #endif | 
|  | #ifdef EMACS_INDENTATION_HELPER | 
|  | } /* Duh. */ | 
|  | #endif | 
|  |  | 
|  | #include "cn-cbor/cn-cbor.h" | 
|  | #include "cbor.h" | 
|  | #include <common.h> | 
|  |  | 
|  | #define CBOR_NO_FLOAT 1 | 
|  |  | 
|  | #define CN_CBOR_FAIL(code) \ | 
|  | do {                   \ | 
|  | pb->err = code;    \ | 
|  | goto fail;         \ | 
|  | } while (0) | 
|  |  | 
|  | CN_CBOR_EXPORT | 
|  | void cn_cbor_dont_free_data(cn_cbor *cbor) | 
|  | { | 
|  | cbor->flags |= CN_CBOR_FL_EXT_DATA; | 
|  | } | 
|  |  | 
|  | CN_CBOR_EXPORT | 
|  | void cn_cbor_free(cn_cbor *cb CBOR_CONTEXT) | 
|  | { | 
|  | cn_cbor *p = (cn_cbor *)cb; | 
|  | assert(!p || !p->parent); | 
|  | while (p) { | 
|  | cn_cbor *p1; | 
|  | while ((p1 = p->first_child)) { /* go down */ | 
|  | p = p1; | 
|  | } | 
|  | if (!(p1 = p->next)) { /* go up next */ | 
|  | if ((p1 = p->parent)) { | 
|  | p1->first_child = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | if ((p->flags & CN_CBOR_FL_EXT_DATA) == 0) { | 
|  | switch (p->type) { | 
|  | case CN_CBOR_BYTES: | 
|  | case CN_CBOR_TEXT: | 
|  | CN_CBOR_FREE_CONTEXT((void *) p->v.bytes); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  | if ((p->flags & CN_CBOR_FL_EXT_SELF) == 0) { | 
|  | CN_CBOR_FREE_CONTEXT(p); | 
|  | } | 
|  | p = p1; | 
|  | } | 
|  | } | 
|  |  | 
|  | #ifndef CBOR_NO_FLOAT | 
|  | static double decode_half(int half) | 
|  | { | 
|  | int exp = (half >> 10) & 0x1f; | 
|  | int mant = half & 0x3ff; | 
|  | double val; | 
|  | if (exp == 0) { | 
|  | val = ldexp(mant, -24); | 
|  | } | 
|  | else if (exp != 31) { | 
|  | val = ldexp(mant + 1024, exp - 25); | 
|  | } | 
|  | else { | 
|  | val = mant == 0 ? INFINITY : NAN; | 
|  | } | 
|  | return half & 0x8000 ? -val : val; | 
|  | } | 
|  | #endif /* CBOR_NO_FLOAT */ | 
|  |  | 
|  | #define ntoh8p(p) (*(unsigned char *)(p)) | 
|  |  | 
|  | #ifndef CBOR_ALIGN_READS | 
|  | #define ntoh16p(p) (ntohs(*(unsigned short *)(p))) | 
|  | #define ntoh32p(p) (ntohl(*(unsigned long *)(p))) | 
|  | #else | 
|  | static uint16_t ntoh16p(unsigned char *p) | 
|  | { | 
|  | uint16_t tmp; | 
|  | memcpy(&tmp, p, sizeof(tmp)); | 
|  | return ntohs(tmp); | 
|  | } | 
|  |  | 
|  | static uint32_t ntoh32p(unsigned char *p) | 
|  | { | 
|  | uint32_t tmp; | 
|  | memcpy(&tmp, p, sizeof(tmp)); | 
|  | return ntohl(tmp); | 
|  | } | 
|  | #endif /* CBOR_ALIGN_READS */ | 
|  |  | 
|  | static uint64_t ntoh64p(unsigned char *p) | 
|  | { | 
|  | uint64_t ret = ntoh32p(p); | 
|  | ret <<= 32; | 
|  | ret += ntoh32p(p + 4); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static cn_cbor_type mt_trans[] = { | 
|  | CN_CBOR_UINT, | 
|  | CN_CBOR_INT, | 
|  | CN_CBOR_BYTES, | 
|  | CN_CBOR_TEXT, | 
|  | CN_CBOR_ARRAY, | 
|  | CN_CBOR_MAP, | 
|  | CN_CBOR_TAG, | 
|  | CN_CBOR_SIMPLE, | 
|  | }; | 
|  |  | 
|  | struct parse_buf { | 
|  | unsigned char *buf; | 
|  | unsigned char *ebuf; | 
|  | cn_cbor_error err; | 
|  | }; | 
|  |  | 
|  | #define TAKE(pos, ebuf, n, stmt)               \ | 
|  | if ((n) > (size_t)((ebuf) - (pos)))          \ | 
|  | CN_CBOR_FAIL(CN_CBOR_ERR_OUT_OF_DATA); \ | 
|  | stmt;                                      \ | 
|  | (pos) += (n); | 
|  |  | 
|  | static cn_cbor *decode_item(struct parse_buf *pb CBOR_CONTEXT, cn_cbor *top_parent) | 
|  | { | 
|  | unsigned char *pos = pb->buf; | 
|  | unsigned char *ebuf = pb->ebuf; | 
|  | cn_cbor *parent = top_parent; | 
|  | int ib = 0; | 
|  | unsigned int mt = 0; | 
|  | int ai = 0; | 
|  | uint64_t val = 0; | 
|  | cn_cbor *cb = NULL; | 
|  | #ifndef CBOR_NO_FLOAT | 
|  | union { | 
|  | float f; | 
|  | uint32_t u; | 
|  | } u32; | 
|  | union { | 
|  | double d; | 
|  | uint64_t u; | 
|  | } u64; | 
|  | #endif /* CBOR_NO_FLOAT */ | 
|  |  | 
|  | again: | 
|  | TAKE(pos, ebuf, 1, ib = ntoh8p(pos)); | 
|  | if (ib == IB_BREAK) { | 
|  | if (!(parent->flags & CN_CBOR_FL_INDEF)) { | 
|  | CN_CBOR_FAIL(CN_CBOR_ERR_BREAK_OUTSIDE_INDEF); | 
|  | } | 
|  | switch (parent->type) { | 
|  | case CN_CBOR_BYTES: | 
|  | case CN_CBOR_TEXT: | 
|  | parent->type += 2; /* CN_CBOR_* -> CN_CBOR_*_CHUNKED */ | 
|  | break; | 
|  |  | 
|  | case CN_CBOR_MAP: | 
|  | if (parent->length & 1) { | 
|  | CN_CBOR_FAIL(CN_CBOR_ERR_ODD_SIZE_INDEF_MAP); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case CN_CBOR_ARRAY: | 
|  | break; | 
|  |  | 
|  | default: | 
|  | CN_CBOR_FAIL(CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING); | 
|  | } | 
|  | goto complete; | 
|  | } | 
|  | mt = ib >> 5; | 
|  | ai = ib & 0x1f; | 
|  | val = ai; | 
|  |  | 
|  | cb = CN_CALLOC_CONTEXT(); | 
|  | if (!cb) { | 
|  | CN_CBOR_FAIL(CN_CBOR_ERR_OUT_OF_MEMORY); | 
|  | } | 
|  |  | 
|  | cb->type = mt_trans[mt]; | 
|  |  | 
|  | cb->parent = parent; | 
|  | if (parent->last_child) { | 
|  | parent->last_child->next = cb; | 
|  | } | 
|  | else { | 
|  | parent->first_child = cb; | 
|  | } | 
|  | parent->last_child = cb; | 
|  | parent->length++; | 
|  |  | 
|  | switch (ai) { | 
|  | case AI_1: | 
|  | TAKE(pos, ebuf, 1, val = ntoh8p(pos)); | 
|  | break; | 
|  | case AI_2: | 
|  | TAKE(pos, ebuf, 2, val = ntoh16p(pos)); | 
|  | break; | 
|  | case AI_4: | 
|  | TAKE(pos, ebuf, 4, val = ntoh32p(pos)); | 
|  | break; | 
|  | case AI_8: | 
|  | TAKE(pos, ebuf, 8, val = ntoh64p(pos)); | 
|  | break; | 
|  | case 28: | 
|  | case 29: | 
|  | case 30: | 
|  | CN_CBOR_FAIL(CN_CBOR_ERR_RESERVED_AI); | 
|  | case AI_INDEF: | 
|  | if ((mt - MT_BYTES) <= MT_MAP) { | 
|  | cb->flags |= CN_CBOR_FL_INDEF; | 
|  | goto push; | 
|  | } | 
|  | else { | 
|  | CN_CBOR_FAIL(CN_CBOR_ERR_MT_UNDEF_FOR_INDEF); | 
|  | } | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | // process content | 
|  | switch (mt) { | 
|  | case MT_UNSIGNED: | 
|  | cb->v.uint = val; /* to do: Overflow check */ | 
|  | break; | 
|  | case MT_NEGATIVE: | 
|  | cb->v.sint = ~val; /* to do: Overflow check */ | 
|  | break; | 
|  | case MT_BYTES: | 
|  | case MT_TEXT: | 
|  | cb->v.str = (char *)pos; | 
|  | cb->length = (size_t)val; | 
|  | cb->flags |= CN_CBOR_FL_EXT_DATA; | 
|  | TAKE(pos, ebuf, val, ;); | 
|  | break; | 
|  | case MT_MAP: | 
|  | val <<= 1; | 
|  | /* fall through */ | 
|  | case MT_ARRAY: | 
|  | if ((cb->v.count = val)) { | 
|  | cb->flags |= CN_CBOR_FL_COUNT; | 
|  | goto push; | 
|  | } | 
|  | break; | 
|  | case MT_TAG: | 
|  | cb->v.uint = val; | 
|  | goto push; | 
|  | case MT_PRIM: | 
|  | switch (ai) { | 
|  | case VAL_FALSE: | 
|  | cb->type = CN_CBOR_FALSE; | 
|  | break; | 
|  | case VAL_TRUE: | 
|  | cb->type = CN_CBOR_TRUE; | 
|  | break; | 
|  | case VAL_NIL: | 
|  | cb->type = CN_CBOR_NULL; | 
|  | break; | 
|  | case VAL_UNDEF: | 
|  | cb->type = CN_CBOR_UNDEF; | 
|  | break; | 
|  | case AI_2: | 
|  | #ifndef CBOR_NO_FLOAT | 
|  | cb->type = CN_CBOR_DOUBLE; | 
|  | cb->v.dbl = decode_half((int) val); | 
|  | #else  /*  CBOR_NO_FLOAT */ | 
|  | CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED); | 
|  | #endif /*  CBOR_NO_FLOAT */ | 
|  | break; | 
|  | case AI_4: | 
|  | #ifndef CBOR_NO_FLOAT | 
|  | cb->type = CN_CBOR_DOUBLE; | 
|  | u32.u = (uint32_t)val; | 
|  | cb->v.dbl = u32.f; | 
|  | #else  /*  CBOR_NO_FLOAT */ | 
|  | CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED); | 
|  | #endif /*  CBOR_NO_FLOAT */ | 
|  | break; | 
|  | case AI_8: | 
|  | #ifndef CBOR_NO_FLOAT | 
|  | cb->type = CN_CBOR_DOUBLE; | 
|  | u64.u = val; | 
|  | cb->v.dbl = u64.d; | 
|  | #else  /*  CBOR_NO_FLOAT */ | 
|  | CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED); | 
|  | #endif /*  CBOR_NO_FLOAT */ | 
|  | break; | 
|  | default: | 
|  | cb->v.uint = val; | 
|  | if (24 <= val && val < 32) { | 
|  | CN_CBOR_FAIL(CN_CBOR_ERR_INVALID_PARAMETER); | 
|  | } | 
|  | break; | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | CN_CBOR_FAIL(CN_CBOR_ERR_INVALID_PARAMETER); | 
|  | } | 
|  |  | 
|  | fill: /* emulate loops */ | 
|  | if (parent->flags & CN_CBOR_FL_INDEF) { | 
|  | if (parent->type == CN_CBOR_BYTES || parent->type == CN_CBOR_TEXT) { | 
|  | if (cb->type != parent->type) { | 
|  | CN_CBOR_FAIL(CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING); | 
|  | } | 
|  | } | 
|  | goto again; | 
|  | } | 
|  | if (parent->flags & CN_CBOR_FL_COUNT) { | 
|  | if (--parent->v.count) { | 
|  | goto again; | 
|  | } | 
|  | } | 
|  | /* so we are done filling parent. */ | 
|  | complete: /* emulate return from call */ | 
|  | if (parent == top_parent) { | 
|  | if (pos != ebuf) { | 
|  | /* XXX do this outside */ | 
|  | CN_CBOR_FAIL(CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED); | 
|  | } | 
|  | pb->buf = pos; | 
|  | return cb; | 
|  | } | 
|  | cb = parent; | 
|  | parent = parent->parent; | 
|  | goto fill; | 
|  | push: /* emulate recursive call */ | 
|  | parent = cb; | 
|  | goto again; | 
|  | fail: | 
|  | pb->buf = pos; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | CN_CBOR_EXPORT | 
|  | cn_cbor *cn_cbor_decode(const unsigned char *buf, size_t len CBOR_CONTEXT, cn_cbor_errback *errp) | 
|  | { | 
|  | cn_cbor catcher = {CN_CBOR_INVALID, 0, {0}, 0, NULL, NULL, NULL, NULL}; | 
|  | struct parse_buf pb; | 
|  | cn_cbor *ret = NULL; | 
|  |  | 
|  | pb.buf = (unsigned char *)buf; | 
|  | pb.ebuf = (unsigned char *)buf + len; | 
|  | pb.err = CN_CBOR_NO_ERROR; | 
|  | ret = decode_item(&pb CBOR_CONTEXT_PARAM, &catcher); | 
|  | if (ret != NULL) { | 
|  | /* mark as top node */ | 
|  | ret->parent = NULL; | 
|  | } | 
|  | else { | 
|  | if (catcher.first_child) { | 
|  | catcher.first_child->parent = 0; | 
|  | cn_cbor_free(catcher.first_child CBOR_CONTEXT_PARAM); | 
|  | } | 
|  | // fail: | 
|  | if (errp) { | 
|  | errp->err = pb.err; | 
|  | errp->pos = pb.buf - (unsigned char *)buf; | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | #ifdef __cplusplus | 
|  | } | 
|  | #endif |