Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 1 | #ifndef CN_CBOR_C |
| 2 | #define CN_CBOR_C |
| 3 | |
| 4 | #ifdef __cplusplus |
| 5 | extern "C" { |
| 6 | #endif |
| 7 | #ifdef EMACS_INDENTATION_HELPER |
| 8 | } /* Duh. */ |
| 9 | #endif |
| 10 | |
| 11 | #include <stdlib.h> |
| 12 | #include <stdint.h> |
| 13 | #include <string.h> |
| 14 | #include <assert.h> |
| 15 | #include <math.h> |
Joe Hildebrand | 60a86fe | 2015-04-02 11:08:09 -0600 | [diff] [blame] | 16 | #include <arpa/inet.h> // needed for ntohl (e.g.) on Linux |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 17 | |
Joe Hildebrand | 7c6c356 | 2015-03-31 00:21:21 -0600 | [diff] [blame] | 18 | #include "cn-cbor/cn-cbor.h" |
Joe Hildebrand | ef8d277 | 2015-03-03 07:00:20 +0100 | [diff] [blame] | 19 | #include "cbor.h" |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 20 | |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 21 | #define CN_CBOR_FAIL(code) do { pb->err = code; goto fail; } while(0) |
| 22 | |
Joe Hildebrand | 45e9ad9 | 2015-08-26 10:37:02 -0600 | [diff] [blame] | 23 | void cn_cbor_free(cn_cbor* cb CBOR_CONTEXT) { |
| 24 | cn_cbor* p = cb; |
| 25 | assert(!p || !p->parent); |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 26 | while (p) { |
| 27 | cn_cbor* p1; |
| 28 | while ((p1 = p->first_child)) { /* go down */ |
| 29 | p = p1; |
| 30 | } |
| 31 | if (!(p1 = p->next)) { /* go up next */ |
| 32 | if ((p1 = p->parent)) |
| 33 | p1->first_child = 0; |
| 34 | } |
Joe Hildebrand | beee57d | 2015-03-30 23:06:46 -0600 | [diff] [blame] | 35 | CN_CBOR_FREE_CONTEXT(p); |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 36 | p = p1; |
| 37 | } |
| 38 | } |
| 39 | |
Olaf Bergmann | 3303413 | 2015-04-24 17:12:40 +0200 | [diff] [blame] | 40 | #ifndef CBOR_NO_FLOAT |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 41 | static double decode_half(int half) { |
| 42 | int exp = (half >> 10) & 0x1f; |
| 43 | int mant = half & 0x3ff; |
| 44 | double val; |
| 45 | if (exp == 0) val = ldexp(mant, -24); |
| 46 | else if (exp != 31) val = ldexp(mant + 1024, exp - 25); |
| 47 | else val = mant == 0 ? INFINITY : NAN; |
| 48 | return half & 0x8000 ? -val : val; |
| 49 | } |
Olaf Bergmann | 3303413 | 2015-04-24 17:12:40 +0200 | [diff] [blame] | 50 | #endif /* CBOR_NO_FLOAT */ |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 51 | |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 52 | #define ntoh8p(p) (*(unsigned char*)(p)) |
Kaspar Schleiser | 6afc222 | 2018-03-23 22:10:41 +0100 | [diff] [blame] | 53 | |
Kaspar Schleiser | d458c34 | 2018-03-23 22:14:36 +0100 | [diff] [blame] | 54 | #ifndef CBOR_ALIGN_READS |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 55 | #define ntoh16p(p) (ntohs(*(unsigned short*)(p))) |
Max Bires | 68f26ba | 2019-03-27 02:15:24 -0700 | [diff] [blame] | 56 | #define ntoh32p(p) (ntohl(*(uint32_t*)(p))) |
Kaspar Schleiser | 6afc222 | 2018-03-23 22:10:41 +0100 | [diff] [blame] | 57 | #else |
Kaspar Schleiser | 894e0b4 | 2018-03-16 15:33:00 +0100 | [diff] [blame] | 58 | static uint16_t ntoh16p(unsigned char *p) { |
| 59 | uint16_t tmp; |
| 60 | memcpy(&tmp, p, sizeof(tmp)); |
| 61 | return ntohs(tmp); |
| 62 | } |
| 63 | |
| 64 | static uint32_t ntoh32p(unsigned char *p) { |
| 65 | uint32_t tmp; |
| 66 | memcpy(&tmp, p, sizeof(tmp)); |
| 67 | return ntohl(tmp); |
| 68 | } |
Kaspar Schleiser | d458c34 | 2018-03-23 22:14:36 +0100 | [diff] [blame] | 69 | #endif /* CBOR_ALIGN_READS */ |
Kaspar Schleiser | 894e0b4 | 2018-03-16 15:33:00 +0100 | [diff] [blame] | 70 | |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 71 | static uint64_t ntoh64p(unsigned char *p) { |
| 72 | uint64_t ret = ntoh32p(p); |
| 73 | ret <<= 32; |
| 74 | ret += ntoh32p(p+4); |
| 75 | return ret; |
| 76 | } |
| 77 | |
| 78 | static cn_cbor_type mt_trans[] = { |
| 79 | CN_CBOR_UINT, CN_CBOR_INT, |
| 80 | CN_CBOR_BYTES, CN_CBOR_TEXT, |
| 81 | CN_CBOR_ARRAY, CN_CBOR_MAP, |
| 82 | CN_CBOR_TAG, CN_CBOR_SIMPLE, |
| 83 | }; |
| 84 | |
| 85 | struct parse_buf { |
| 86 | unsigned char *buf; |
| 87 | unsigned char *ebuf; |
| 88 | cn_cbor_error err; |
| 89 | }; |
| 90 | |
| 91 | #define TAKE(pos, ebuf, n, stmt) \ |
Carsten Bormann | 85d764d | 2015-03-02 10:47:13 +0100 | [diff] [blame] | 92 | if (n > (size_t)(ebuf - pos)) \ |
Joe Hildebrand | d9efcab | 2015-03-02 09:25:05 +0100 | [diff] [blame] | 93 | CN_CBOR_FAIL(CN_CBOR_ERR_OUT_OF_DATA); \ |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 94 | stmt; \ |
| 95 | pos += n; |
| 96 | |
Joe Hildebrand | beee57d | 2015-03-30 23:06:46 -0600 | [diff] [blame] | 97 | static cn_cbor *decode_item (struct parse_buf *pb CBOR_CONTEXT, cn_cbor* top_parent) { |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 98 | unsigned char *pos = pb->buf; |
| 99 | unsigned char *ebuf = pb->ebuf; |
| 100 | cn_cbor* parent = top_parent; |
Joe Hildebrand | 0306789 | 2015-03-03 06:56:45 +0100 | [diff] [blame] | 101 | int ib; |
| 102 | unsigned int mt; |
| 103 | int ai; |
| 104 | uint64_t val; |
Joe Hildebrand | beee57d | 2015-03-30 23:06:46 -0600 | [diff] [blame] | 105 | cn_cbor* cb = NULL; |
Olaf Bergmann | 3303413 | 2015-04-24 17:12:40 +0200 | [diff] [blame] | 106 | #ifndef CBOR_NO_FLOAT |
Joe Hildebrand | 0306789 | 2015-03-03 06:56:45 +0100 | [diff] [blame] | 107 | union { |
| 108 | float f; |
| 109 | uint32_t u; |
| 110 | } u32; |
| 111 | union { |
| 112 | double d; |
| 113 | uint64_t u; |
| 114 | } u64; |
Olaf Bergmann | 3303413 | 2015-04-24 17:12:40 +0200 | [diff] [blame] | 115 | #endif /* CBOR_NO_FLOAT */ |
Joe Hildebrand | 0306789 | 2015-03-03 06:56:45 +0100 | [diff] [blame] | 116 | |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 117 | again: |
Joe Hildebrand | 0306789 | 2015-03-03 06:56:45 +0100 | [diff] [blame] | 118 | TAKE(pos, ebuf, 1, ib = ntoh8p(pos) ); |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 119 | if (ib == IB_BREAK) { |
| 120 | if (!(parent->flags & CN_CBOR_FL_INDEF)) |
| 121 | CN_CBOR_FAIL(CN_CBOR_ERR_BREAK_OUTSIDE_INDEF); |
| 122 | switch (parent->type) { |
| 123 | case CN_CBOR_BYTES: case CN_CBOR_TEXT: |
| 124 | parent->type += 2; /* CN_CBOR_* -> CN_CBOR_*_CHUNKED */ |
| 125 | break; |
| 126 | case CN_CBOR_MAP: |
| 127 | if (parent->length & 1) |
| 128 | CN_CBOR_FAIL(CN_CBOR_ERR_ODD_SIZE_INDEF_MAP); |
| 129 | default:; |
| 130 | } |
| 131 | goto complete; |
| 132 | } |
Joe Hildebrand | 0306789 | 2015-03-03 06:56:45 +0100 | [diff] [blame] | 133 | mt = ib >> 5; |
| 134 | ai = ib & 0x1f; |
| 135 | val = ai; |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 136 | |
Joe Hildebrand | beee57d | 2015-03-30 23:06:46 -0600 | [diff] [blame] | 137 | cb = CN_CALLOC_CONTEXT(); |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 138 | if (!cb) |
| 139 | CN_CBOR_FAIL(CN_CBOR_ERR_OUT_OF_MEMORY); |
| 140 | |
| 141 | cb->type = mt_trans[mt]; |
| 142 | |
| 143 | cb->parent = parent; |
| 144 | if (parent->last_child) { |
| 145 | parent->last_child->next = cb; |
| 146 | } else { |
| 147 | parent->first_child = cb; |
| 148 | } |
| 149 | parent->last_child = cb; |
| 150 | parent->length++; |
| 151 | |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 152 | switch (ai) { |
| 153 | case AI_1: TAKE(pos, ebuf, 1, val = ntoh8p(pos)) ; break; |
| 154 | case AI_2: TAKE(pos, ebuf, 2, val = ntoh16p(pos)) ; break; |
| 155 | case AI_4: TAKE(pos, ebuf, 4, val = ntoh32p(pos)) ; break; |
| 156 | case AI_8: TAKE(pos, ebuf, 8, val = ntoh64p(pos)) ; break; |
| 157 | case 28: case 29: case 30: CN_CBOR_FAIL(CN_CBOR_ERR_RESERVED_AI); |
| 158 | case AI_INDEF: |
| 159 | if ((mt - MT_BYTES) <= MT_MAP) { |
| 160 | cb->flags |= CN_CBOR_FL_INDEF; |
| 161 | goto push; |
| 162 | } else { |
| 163 | CN_CBOR_FAIL(CN_CBOR_ERR_MT_UNDEF_FOR_INDEF); |
| 164 | } |
| 165 | } |
| 166 | // process content |
| 167 | switch (mt) { |
| 168 | case MT_UNSIGNED: |
| 169 | cb->v.uint = val; /* to do: Overflow check */ |
| 170 | break; |
| 171 | case MT_NEGATIVE: |
| 172 | cb->v.sint = ~val; /* to do: Overflow check */ |
| 173 | break; |
| 174 | case MT_BYTES: case MT_TEXT: |
| 175 | cb->v.str = (char *) pos; |
| 176 | cb->length = val; |
Joe Hildebrand | beee57d | 2015-03-30 23:06:46 -0600 | [diff] [blame] | 177 | TAKE(pos, ebuf, val, ;); |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 178 | break; |
| 179 | case MT_MAP: |
| 180 | val <<= 1; |
| 181 | /* fall through */ |
| 182 | case MT_ARRAY: |
| 183 | if ((cb->v.count = val)) { |
| 184 | cb->flags |= CN_CBOR_FL_COUNT; |
| 185 | goto push; |
| 186 | } |
| 187 | break; |
| 188 | case MT_TAG: |
| 189 | cb->v.uint = val; |
| 190 | goto push; |
| 191 | case MT_PRIM: |
| 192 | switch (ai) { |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 193 | case VAL_FALSE: cb->type = CN_CBOR_FALSE; break; |
Joe Hildebrand | a995401 | 2015-03-31 23:00:43 -0600 | [diff] [blame] | 194 | case VAL_TRUE: cb->type = CN_CBOR_TRUE; break; |
| 195 | case VAL_NIL: cb->type = CN_CBOR_NULL; break; |
| 196 | case VAL_UNDEF: cb->type = CN_CBOR_UNDEF; break; |
Olaf Bergmann | 3303413 | 2015-04-24 17:12:40 +0200 | [diff] [blame] | 197 | case AI_2: |
| 198 | #ifndef CBOR_NO_FLOAT |
| 199 | cb->type = CN_CBOR_DOUBLE; |
| 200 | cb->v.dbl = decode_half(val); |
| 201 | #else /* CBOR_NO_FLOAT */ |
| 202 | CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED); |
| 203 | #endif /* CBOR_NO_FLOAT */ |
| 204 | break; |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 205 | case AI_4: |
Olaf Bergmann | 3303413 | 2015-04-24 17:12:40 +0200 | [diff] [blame] | 206 | #ifndef CBOR_NO_FLOAT |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 207 | cb->type = CN_CBOR_DOUBLE; |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 208 | u32.u = val; |
| 209 | cb->v.dbl = u32.f; |
Olaf Bergmann | 3303413 | 2015-04-24 17:12:40 +0200 | [diff] [blame] | 210 | #else /* CBOR_NO_FLOAT */ |
| 211 | CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED); |
| 212 | #endif /* CBOR_NO_FLOAT */ |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 213 | break; |
| 214 | case AI_8: |
Olaf Bergmann | 3303413 | 2015-04-24 17:12:40 +0200 | [diff] [blame] | 215 | #ifndef CBOR_NO_FLOAT |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 216 | cb->type = CN_CBOR_DOUBLE; |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 217 | u64.u = val; |
| 218 | cb->v.dbl = u64.d; |
Olaf Bergmann | 3303413 | 2015-04-24 17:12:40 +0200 | [diff] [blame] | 219 | #else /* CBOR_NO_FLOAT */ |
| 220 | CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED); |
| 221 | #endif /* CBOR_NO_FLOAT */ |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 222 | break; |
| 223 | default: cb->v.uint = val; |
| 224 | } |
| 225 | } |
| 226 | fill: /* emulate loops */ |
| 227 | if (parent->flags & CN_CBOR_FL_INDEF) { |
| 228 | if (parent->type == CN_CBOR_BYTES || parent->type == CN_CBOR_TEXT) |
| 229 | if (cb->type != parent->type) |
| 230 | CN_CBOR_FAIL(CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING); |
| 231 | goto again; |
| 232 | } |
| 233 | if (parent->flags & CN_CBOR_FL_COUNT) { |
| 234 | if (--parent->v.count) |
| 235 | goto again; |
| 236 | } |
| 237 | /* so we are done filling parent. */ |
| 238 | complete: /* emulate return from call */ |
| 239 | if (parent == top_parent) { |
| 240 | if (pos != ebuf) /* XXX do this outside */ |
| 241 | CN_CBOR_FAIL(CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED); |
| 242 | pb->buf = pos; |
| 243 | return cb; |
| 244 | } |
| 245 | cb = parent; |
| 246 | parent = parent->parent; |
| 247 | goto fill; |
| 248 | push: /* emulate recursive call */ |
| 249 | parent = cb; |
| 250 | goto again; |
| 251 | fail: |
| 252 | pb->buf = pos; |
| 253 | return 0; |
| 254 | } |
| 255 | |
Joe Hildebrand | 45e9ad9 | 2015-08-26 10:37:02 -0600 | [diff] [blame] | 256 | cn_cbor* cn_cbor_decode(const unsigned char* buf, size_t len CBOR_CONTEXT, cn_cbor_errback *errp) { |
Joe Hildebrand | 0306789 | 2015-03-03 06:56:45 +0100 | [diff] [blame] | 257 | cn_cbor catcher = {CN_CBOR_INVALID, 0, {0}, 0, NULL, NULL, NULL, NULL}; |
Joe Hildebrand | beee57d | 2015-03-30 23:06:46 -0600 | [diff] [blame] | 258 | struct parse_buf pb; |
| 259 | cn_cbor* ret; |
| 260 | |
| 261 | pb.buf = (unsigned char *)buf; |
| 262 | pb.ebuf = (unsigned char *)buf+len; |
| 263 | pb.err = CN_CBOR_NO_ERROR; |
| 264 | ret = decode_item(&pb CBOR_CONTEXT_PARAM, &catcher); |
Joe Hildebrand | 0306789 | 2015-03-03 06:56:45 +0100 | [diff] [blame] | 265 | if (ret != NULL) { |
| 266 | /* mark as top node */ |
| 267 | ret->parent = NULL; |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 268 | } else { |
| 269 | if (catcher.first_child) { |
| 270 | catcher.first_child->parent = 0; |
Joe Hildebrand | beee57d | 2015-03-30 23:06:46 -0600 | [diff] [blame] | 271 | cn_cbor_free(catcher.first_child CBOR_CONTEXT_PARAM); |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 272 | } |
Joe Hildebrand | beee57d | 2015-03-30 23:06:46 -0600 | [diff] [blame] | 273 | //fail: |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 274 | if (errp) { |
| 275 | errp->err = pb.err; |
| 276 | errp->pos = pb.buf - (unsigned char *)buf; |
| 277 | } |
Joe Hildebrand | 0306789 | 2015-03-03 06:56:45 +0100 | [diff] [blame] | 278 | return NULL; |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 279 | } |
| 280 | return ret; |
| 281 | } |
| 282 | |
Carsten Bormann | 64e2b52 | 2015-02-17 18:52:34 +0100 | [diff] [blame] | 283 | #ifdef __cplusplus |
| 284 | } |
| 285 | #endif |
| 286 | |
| 287 | #endif /* CN_CBOR_C */ |