File Coverage

amqp_table.c
Criterion Covered Total %
statement 0 344 0.0
branch 0 144 0.0
condition n/a
subroutine n/a
pod n/a
total 0 488 0.0


line stmt bran cond sub pod time code
1             // Copyright 2007 - 2021, Alan Antonuk and the rabbitmq-c contributors.
2             // SPDX-License-Identifier: mit
3              
4             #ifdef HAVE_CONFIG_H
5             #include "config.h"
6             #endif
7              
8             #include "amqp_private.h"
9             #include "amqp_table.h"
10             #include
11             #include
12             #include
13             #include
14             #include
15              
16             #define INITIAL_ARRAY_SIZE 16
17             #define INITIAL_TABLE_SIZE 16
18             #define TABLE_DEPTH_LIMIT 100
19              
20             static int amqp_decode_field_value(amqp_bytes_t encoded, amqp_pool_t *pool,
21             amqp_field_value_t *entry, size_t *offset,
22             int depth);
23              
24             static int amqp_encode_field_value(amqp_bytes_t encoded,
25             amqp_field_value_t *entry, size_t *offset);
26              
27             /*---------------------------------------------------------------------------*/
28              
29 0           static int amqp_decode_array(amqp_bytes_t encoded, amqp_pool_t *pool,
30             amqp_array_t *output, size_t *offset, int depth) {
31             uint32_t arraysize;
32 0           int num_entries = 0;
33 0           int allocated_entries = INITIAL_ARRAY_SIZE;
34             amqp_field_value_t *entries;
35             size_t limit;
36             int res;
37              
38 0 0         if (!amqp_decode_32(encoded, offset, &arraysize)) {
39 0           return AMQP_STATUS_BAD_AMQP_DATA;
40             }
41              
42 0 0         if (arraysize + *offset > encoded.len) {
43 0           return AMQP_STATUS_BAD_AMQP_DATA;
44             }
45              
46 0           entries = malloc(allocated_entries * sizeof(amqp_field_value_t));
47 0 0         if (entries == NULL) {
48 0           return AMQP_STATUS_NO_MEMORY;
49             }
50              
51 0           limit = *offset + arraysize;
52 0 0         while (*offset < limit) {
53 0 0         if (num_entries >= allocated_entries) {
54             void *newentries;
55 0           allocated_entries = allocated_entries * 2;
56             newentries =
57 0           realloc(entries, allocated_entries * sizeof(amqp_field_value_t));
58 0           res = AMQP_STATUS_NO_MEMORY;
59 0 0         if (newentries == NULL) {
60 0           goto out;
61             }
62              
63 0           entries = newentries;
64             }
65              
66 0           res = amqp_decode_field_value(encoded, pool, &entries[num_entries], offset,
67             depth);
68 0 0         if (res < 0) {
69 0           goto out;
70             }
71              
72 0           num_entries++;
73             }
74              
75 0           output->num_entries = num_entries;
76 0           output->entries =
77 0           amqp_pool_alloc(pool, num_entries * sizeof(amqp_field_value_t));
78             /* NULL is legitimate if we requested a zero-length block. */
79 0 0         if (output->entries == NULL) {
80 0 0         if (num_entries == 0) {
81 0           res = AMQP_STATUS_OK;
82             } else {
83 0           res = AMQP_STATUS_NO_MEMORY;
84             }
85 0           goto out;
86             }
87              
88 0           memcpy(output->entries, entries, num_entries * sizeof(amqp_field_value_t));
89 0           res = AMQP_STATUS_OK;
90              
91 0           out:
92 0           free(entries);
93 0           return res;
94             }
95              
96 0           static int amqp_decode_table_internal(amqp_bytes_t encoded, amqp_pool_t *pool,
97             amqp_table_t *output, size_t *offset,
98             int depth) {
99             uint32_t tablesize;
100 0           int num_entries = 0;
101             amqp_table_entry_t *entries;
102 0           int allocated_entries = INITIAL_TABLE_SIZE;
103             size_t limit;
104             int res;
105              
106 0 0         if (!amqp_decode_32(encoded, offset, &tablesize)) {
107 0           return AMQP_STATUS_BAD_AMQP_DATA;
108             }
109              
110 0 0         if (tablesize + *offset > encoded.len) {
111 0           return AMQP_STATUS_BAD_AMQP_DATA;
112             }
113              
114 0           entries = malloc(allocated_entries * sizeof(amqp_table_entry_t));
115 0 0         if (entries == NULL) {
116 0           return AMQP_STATUS_NO_MEMORY;
117             }
118              
119 0           limit = *offset + tablesize;
120 0 0         while (*offset < limit) {
121             uint8_t keylen;
122              
123 0           res = AMQP_STATUS_BAD_AMQP_DATA;
124 0 0         if (!amqp_decode_8(encoded, offset, &keylen)) {
125 0           goto out;
126             }
127              
128 0 0         if (num_entries >= allocated_entries) {
129             void *newentries;
130 0           allocated_entries = allocated_entries * 2;
131             newentries =
132 0           realloc(entries, allocated_entries * sizeof(amqp_table_entry_t));
133 0           res = AMQP_STATUS_NO_MEMORY;
134 0 0         if (newentries == NULL) {
135 0           goto out;
136             }
137              
138 0           entries = newentries;
139             }
140              
141 0           res = AMQP_STATUS_BAD_AMQP_DATA;
142 0 0         if (!amqp_decode_bytes(encoded, offset, &entries[num_entries].key,
143             keylen)) {
144 0           goto out;
145             }
146              
147 0           res = amqp_decode_field_value(encoded, pool, &entries[num_entries].value,
148             offset, depth);
149 0 0         if (res < 0) {
150 0           goto out;
151             }
152              
153 0           num_entries++;
154             }
155              
156 0           output->num_entries = num_entries;
157 0           output->entries =
158 0           amqp_pool_alloc(pool, num_entries * sizeof(amqp_table_entry_t));
159             /* NULL is legitimate if we requested a zero-length block. */
160 0 0         if (output->entries == NULL) {
161 0 0         if (num_entries == 0) {
162 0           res = AMQP_STATUS_OK;
163             } else {
164 0           res = AMQP_STATUS_NO_MEMORY;
165             }
166 0           goto out;
167             }
168              
169 0           memcpy(output->entries, entries, num_entries * sizeof(amqp_table_entry_t));
170 0           res = AMQP_STATUS_OK;
171              
172 0           out:
173 0           free(entries);
174 0           return res;
175             }
176              
177 0           int amqp_decode_table(amqp_bytes_t encoded, amqp_pool_t *pool,
178             amqp_table_t *output, size_t *offset) {
179 0           return amqp_decode_table_internal(encoded, pool, output, offset, 0);
180             }
181              
182 0           static int amqp_decode_field_value(amqp_bytes_t encoded, amqp_pool_t *pool,
183             amqp_field_value_t *entry, size_t *offset,
184             int depth) {
185 0           int res = AMQP_STATUS_BAD_AMQP_DATA;
186              
187 0 0         if (depth > TABLE_DEPTH_LIMIT) {
188 0           return AMQP_STATUS_BAD_AMQP_DATA;
189             }
190              
191 0 0         if (!amqp_decode_8(encoded, offset, &entry->kind)) {
192 0           goto out;
193             }
194              
195             #define TRIVIAL_FIELD_DECODER(bits) \
196             if (!amqp_decode_##bits(encoded, offset, &entry->value.u##bits)) goto out; \
197             break
198             #define SIMPLE_FIELD_DECODER(bits, dest, how) \
199             { \
200             uint##bits##_t val; \
201             if (!amqp_decode_##bits(encoded, offset, &val)) goto out; \
202             entry->value.dest = how; \
203             } \
204             break
205              
206 0           switch (entry->kind) {
207 0           case AMQP_FIELD_KIND_BOOLEAN:
208 0 0         SIMPLE_FIELD_DECODER(8, boolean, val ? 1 : 0);
209              
210 0           case AMQP_FIELD_KIND_I8:
211 0 0         SIMPLE_FIELD_DECODER(8, i8, (int8_t)val);
212 0           case AMQP_FIELD_KIND_U8:
213 0 0         TRIVIAL_FIELD_DECODER(8);
214              
215 0           case AMQP_FIELD_KIND_I16:
216 0 0         SIMPLE_FIELD_DECODER(16, i16, (int16_t)val);
217 0           case AMQP_FIELD_KIND_U16:
218 0 0         TRIVIAL_FIELD_DECODER(16);
219              
220 0           case AMQP_FIELD_KIND_I32:
221 0 0         SIMPLE_FIELD_DECODER(32, i32, (int32_t)val);
222 0           case AMQP_FIELD_KIND_U32:
223 0 0         TRIVIAL_FIELD_DECODER(32);
224              
225 0           case AMQP_FIELD_KIND_I64:
226 0 0         SIMPLE_FIELD_DECODER(64, i64, (int64_t)val);
227 0           case AMQP_FIELD_KIND_U64:
228 0 0         TRIVIAL_FIELD_DECODER(64);
229              
230 0           case AMQP_FIELD_KIND_F32:
231 0 0         TRIVIAL_FIELD_DECODER(32);
232             /* and by punning, f32 magically gets the right value...! */
233              
234 0           case AMQP_FIELD_KIND_F64:
235 0 0         TRIVIAL_FIELD_DECODER(64);
236             /* and by punning, f64 magically gets the right value...! */
237              
238 0           case AMQP_FIELD_KIND_DECIMAL:
239 0           if (!amqp_decode_8(encoded, offset, &entry->value.decimal.decimals) ||
240 0           !amqp_decode_32(encoded, offset, &entry->value.decimal.value)) {
241 0           goto out;
242             }
243 0           break;
244              
245 0           case AMQP_FIELD_KIND_UTF8:
246             /* AMQP_FIELD_KIND_UTF8 and AMQP_FIELD_KIND_BYTES have the
247             same implementation, but different interpretations. */
248             /* fall through */
249             case AMQP_FIELD_KIND_BYTES: {
250             uint32_t len;
251 0           if (!amqp_decode_32(encoded, offset, &len) ||
252 0           !amqp_decode_bytes(encoded, offset, &entry->value.bytes, len)) {
253 0           goto out;
254             }
255 0           break;
256             }
257              
258 0           case AMQP_FIELD_KIND_ARRAY:
259 0           res = amqp_decode_array(encoded, pool, &(entry->value.array), offset,
260             depth + 1);
261 0           goto out;
262              
263 0           case AMQP_FIELD_KIND_TIMESTAMP:
264 0 0         TRIVIAL_FIELD_DECODER(64);
265              
266 0           case AMQP_FIELD_KIND_TABLE:
267 0           res = amqp_decode_table_internal(encoded, pool, &(entry->value.table),
268             offset, depth + 1);
269 0           goto out;
270              
271 0           case AMQP_FIELD_KIND_VOID:
272 0           break;
273              
274 0           default:
275 0           goto out;
276             }
277              
278 0           res = AMQP_STATUS_OK;
279              
280 0           out:
281 0           return res;
282             }
283              
284             /*---------------------------------------------------------------------------*/
285              
286 0           static int amqp_encode_array(amqp_bytes_t encoded, amqp_array_t *input,
287             size_t *offset) {
288 0           size_t start = *offset;
289             int i, res;
290              
291 0           *offset += 4; /* size of the array gets filled in later on */
292              
293 0 0         for (i = 0; i < input->num_entries; i++) {
294 0           res = amqp_encode_field_value(encoded, &input->entries[i], offset);
295 0 0         if (res < 0) {
296 0           goto out;
297             }
298             }
299              
300 0 0         if (!amqp_encode_32(encoded, &start, (uint32_t)(*offset - start - 4))) {
301 0           res = AMQP_STATUS_TABLE_TOO_BIG;
302 0           goto out;
303             }
304              
305 0           res = AMQP_STATUS_OK;
306              
307 0           out:
308 0           return res;
309             }
310              
311 0           int amqp_encode_table(amqp_bytes_t encoded, amqp_table_t *input,
312             size_t *offset) {
313 0           size_t start = *offset;
314             int i, res;
315              
316 0           *offset += 4; /* size of the table gets filled in later on */
317              
318 0 0         for (i = 0; i < input->num_entries; i++) {
319 0 0         if (!amqp_encode_8(encoded, offset, (uint8_t)input->entries[i].key.len)) {
320 0           res = AMQP_STATUS_TABLE_TOO_BIG;
321 0           goto out;
322             }
323              
324 0 0         if (!amqp_encode_bytes(encoded, offset, input->entries[i].key)) {
325 0           res = AMQP_STATUS_TABLE_TOO_BIG;
326 0           goto out;
327             }
328              
329 0           res = amqp_encode_field_value(encoded, &input->entries[i].value, offset);
330 0 0         if (res < 0) {
331 0           goto out;
332             }
333             }
334              
335 0 0         if (!amqp_encode_32(encoded, &start, (uint32_t)(*offset - start - 4))) {
336 0           res = AMQP_STATUS_TABLE_TOO_BIG;
337 0           goto out;
338             }
339              
340 0           res = AMQP_STATUS_OK;
341              
342 0           out:
343 0           return res;
344             }
345              
346 0           static int amqp_encode_field_value(amqp_bytes_t encoded,
347             amqp_field_value_t *entry, size_t *offset) {
348 0           int res = AMQP_STATUS_BAD_AMQP_DATA;
349              
350 0 0         if (!amqp_encode_8(encoded, offset, entry->kind)) {
351 0           goto out;
352             }
353              
354             #define FIELD_ENCODER(bits, val) \
355             if (!amqp_encode_##bits(encoded, offset, val)) { \
356             res = AMQP_STATUS_TABLE_TOO_BIG; \
357             goto out; \
358             } \
359             break
360              
361 0           switch (entry->kind) {
362 0           case AMQP_FIELD_KIND_BOOLEAN:
363 0 0         FIELD_ENCODER(8, entry->value.boolean ? 1 : 0);
364              
365 0           case AMQP_FIELD_KIND_I8:
366 0 0         FIELD_ENCODER(8, entry->value.i8);
367 0           case AMQP_FIELD_KIND_U8:
368 0 0         FIELD_ENCODER(8, entry->value.u8);
369              
370 0           case AMQP_FIELD_KIND_I16:
371 0 0         FIELD_ENCODER(16, entry->value.i16);
372 0           case AMQP_FIELD_KIND_U16:
373 0 0         FIELD_ENCODER(16, entry->value.u16);
374              
375 0           case AMQP_FIELD_KIND_I32:
376 0 0         FIELD_ENCODER(32, entry->value.i32);
377 0           case AMQP_FIELD_KIND_U32:
378 0 0         FIELD_ENCODER(32, entry->value.u32);
379              
380 0           case AMQP_FIELD_KIND_I64:
381 0 0         FIELD_ENCODER(64, entry->value.i64);
382 0           case AMQP_FIELD_KIND_U64:
383 0 0         FIELD_ENCODER(64, entry->value.u64);
384              
385 0           case AMQP_FIELD_KIND_F32:
386             /* by punning, u32 magically gets the right value...! */
387 0 0         FIELD_ENCODER(32, entry->value.u32);
388              
389 0           case AMQP_FIELD_KIND_F64:
390             /* by punning, u64 magically gets the right value...! */
391 0 0         FIELD_ENCODER(64, entry->value.u64);
392              
393 0           case AMQP_FIELD_KIND_DECIMAL:
394 0           if (!amqp_encode_8(encoded, offset, entry->value.decimal.decimals) ||
395 0           !amqp_encode_32(encoded, offset, entry->value.decimal.value)) {
396 0           res = AMQP_STATUS_TABLE_TOO_BIG;
397 0           goto out;
398             }
399 0           break;
400              
401 0           case AMQP_FIELD_KIND_UTF8:
402             /* AMQP_FIELD_KIND_UTF8 and AMQP_FIELD_KIND_BYTES have the
403             same implementation, but different interpretations. */
404             /* fall through */
405             case AMQP_FIELD_KIND_BYTES:
406 0           if (!amqp_encode_32(encoded, offset, (uint32_t)entry->value.bytes.len) ||
407 0           !amqp_encode_bytes(encoded, offset, entry->value.bytes)) {
408 0           res = AMQP_STATUS_TABLE_TOO_BIG;
409 0           goto out;
410             }
411 0           break;
412              
413 0           case AMQP_FIELD_KIND_ARRAY:
414 0           res = amqp_encode_array(encoded, &entry->value.array, offset);
415 0           goto out;
416              
417 0           case AMQP_FIELD_KIND_TIMESTAMP:
418 0 0         FIELD_ENCODER(64, entry->value.u64);
419              
420 0           case AMQP_FIELD_KIND_TABLE:
421 0           res = amqp_encode_table(encoded, &entry->value.table, offset);
422 0           goto out;
423              
424 0           case AMQP_FIELD_KIND_VOID:
425 0           break;
426              
427 0           default:
428 0           res = AMQP_STATUS_INVALID_PARAMETER;
429 0           goto out;
430             }
431              
432 0           res = AMQP_STATUS_OK;
433              
434 0           out:
435 0           return res;
436             }
437              
438             /*---------------------------------------------------------------------------*/
439              
440 0           int amqp_table_entry_cmp(void const *entry1, void const *entry2) {
441 0           amqp_table_entry_t const *p1 = (amqp_table_entry_t const *)entry1;
442 0           amqp_table_entry_t const *p2 = (amqp_table_entry_t const *)entry2;
443              
444             int d;
445             size_t minlen;
446              
447 0           minlen = p1->key.len;
448 0 0         if (p2->key.len < minlen) {
449 0           minlen = p2->key.len;
450             }
451              
452 0           d = memcmp(p1->key.bytes, p2->key.bytes, minlen);
453 0 0         if (d != 0) {
454 0           return d;
455             }
456              
457 0           return (int)p1->key.len - (int)p2->key.len;
458             }
459              
460 0           static int amqp_field_value_clone(const amqp_field_value_t *original,
461             amqp_field_value_t *clone,
462             amqp_pool_t *pool) {
463             int i;
464             int res;
465 0           clone->kind = original->kind;
466              
467 0           switch (clone->kind) {
468 0           case AMQP_FIELD_KIND_BOOLEAN:
469 0           clone->value.boolean = original->value.boolean;
470 0           break;
471              
472 0           case AMQP_FIELD_KIND_I8:
473 0           clone->value.i8 = original->value.i8;
474 0           break;
475              
476 0           case AMQP_FIELD_KIND_U8:
477 0           clone->value.u8 = original->value.u8;
478 0           break;
479              
480 0           case AMQP_FIELD_KIND_I16:
481 0           clone->value.i16 = original->value.i16;
482 0           break;
483              
484 0           case AMQP_FIELD_KIND_U16:
485 0           clone->value.u16 = original->value.u16;
486 0           break;
487              
488 0           case AMQP_FIELD_KIND_I32:
489 0           clone->value.i32 = original->value.i32;
490 0           break;
491              
492 0           case AMQP_FIELD_KIND_U32:
493 0           clone->value.u32 = original->value.u32;
494 0           break;
495              
496 0           case AMQP_FIELD_KIND_I64:
497 0           clone->value.i64 = original->value.i64;
498 0           break;
499              
500 0           case AMQP_FIELD_KIND_U64:
501             case AMQP_FIELD_KIND_TIMESTAMP:
502 0           clone->value.u64 = original->value.u64;
503 0           break;
504              
505 0           case AMQP_FIELD_KIND_F32:
506 0           clone->value.f32 = original->value.f32;
507 0           break;
508              
509 0           case AMQP_FIELD_KIND_F64:
510 0           clone->value.f64 = original->value.f64;
511 0           break;
512              
513 0           case AMQP_FIELD_KIND_DECIMAL:
514 0           clone->value.decimal = original->value.decimal;
515 0           break;
516              
517 0           case AMQP_FIELD_KIND_UTF8:
518             case AMQP_FIELD_KIND_BYTES:
519 0 0         if (0 == original->value.bytes.len) {
520 0           clone->value.bytes = amqp_empty_bytes;
521             } else {
522 0           amqp_pool_alloc_bytes(pool, original->value.bytes.len,
523             &clone->value.bytes);
524 0 0         if (NULL == clone->value.bytes.bytes) {
525 0           return AMQP_STATUS_NO_MEMORY;
526             }
527 0           memcpy(clone->value.bytes.bytes, original->value.bytes.bytes,
528             clone->value.bytes.len);
529             }
530 0           break;
531              
532 0           case AMQP_FIELD_KIND_ARRAY:
533 0 0         if (0 == original->value.array.entries) {
534 0           clone->value.array = amqp_empty_array;
535             } else {
536 0           clone->value.array.num_entries = original->value.array.num_entries;
537 0           clone->value.array.entries = amqp_pool_alloc(
538 0           pool, clone->value.array.num_entries * sizeof(amqp_field_value_t));
539 0 0         if (NULL == clone->value.array.entries) {
540 0           return AMQP_STATUS_NO_MEMORY;
541             }
542              
543 0 0         for (i = 0; i < clone->value.array.num_entries; ++i) {
544 0           res = amqp_field_value_clone(&original->value.array.entries[i],
545 0           &clone->value.array.entries[i], pool);
546 0 0         if (AMQP_STATUS_OK != res) {
547 0           return res;
548             }
549             }
550             }
551 0           break;
552              
553 0           case AMQP_FIELD_KIND_TABLE:
554 0           return amqp_table_clone(&original->value.table, &clone->value.table,
555             pool);
556              
557 0           case AMQP_FIELD_KIND_VOID:
558 0           break;
559              
560 0           default:
561 0           return AMQP_STATUS_INVALID_PARAMETER;
562             }
563              
564 0           return AMQP_STATUS_OK;
565             }
566              
567 0           static int amqp_table_entry_clone(const amqp_table_entry_t *original,
568             amqp_table_entry_t *clone,
569             amqp_pool_t *pool) {
570 0 0         if (0 == original->key.len) {
571 0           return AMQP_STATUS_INVALID_PARAMETER;
572             }
573              
574 0           amqp_pool_alloc_bytes(pool, original->key.len, &clone->key);
575 0 0         if (NULL == clone->key.bytes) {
576 0           return AMQP_STATUS_NO_MEMORY;
577             }
578              
579 0           memcpy(clone->key.bytes, original->key.bytes, clone->key.len);
580              
581 0           return amqp_field_value_clone(&original->value, &clone->value, pool);
582             }
583              
584 0           int amqp_table_clone(const amqp_table_t *original, amqp_table_t *clone,
585             amqp_pool_t *pool) {
586             int i;
587             int res;
588 0           clone->num_entries = original->num_entries;
589 0 0         if (0 == clone->num_entries) {
590 0           *clone = amqp_empty_table;
591 0           return AMQP_STATUS_OK;
592             }
593              
594 0           clone->entries =
595 0           amqp_pool_alloc(pool, clone->num_entries * sizeof(amqp_table_entry_t));
596              
597 0 0         if (NULL == clone->entries) {
598 0           return AMQP_STATUS_NO_MEMORY;
599             }
600              
601 0 0         for (i = 0; i < clone->num_entries; ++i) {
602             res =
603 0           amqp_table_entry_clone(&original->entries[i], &clone->entries[i], pool);
604 0 0         if (AMQP_STATUS_OK != res) {
605 0           goto error_out1;
606             }
607             }
608              
609 0           return AMQP_STATUS_OK;
610              
611 0           error_out1:
612 0           return res;
613             }
614              
615 0           amqp_table_entry_t amqp_table_construct_utf8_entry(const char *key,
616             const char *value) {
617             amqp_table_entry_t ret;
618 0           ret.key = amqp_cstring_bytes(key);
619 0           ret.value.kind = AMQP_FIELD_KIND_UTF8;
620 0           ret.value.value.bytes = amqp_cstring_bytes(value);
621 0           return ret;
622             }
623              
624 0           amqp_table_entry_t amqp_table_construct_table_entry(const char *key,
625             const amqp_table_t *value) {
626             amqp_table_entry_t ret;
627 0           ret.key = amqp_cstring_bytes(key);
628 0           ret.value.kind = AMQP_FIELD_KIND_TABLE;
629 0           ret.value.value.table = *value;
630 0           return ret;
631             }
632              
633 0           amqp_table_entry_t amqp_table_construct_bool_entry(const char *key,
634             const int value) {
635             amqp_table_entry_t ret;
636 0           ret.key = amqp_cstring_bytes(key);
637 0           ret.value.kind = AMQP_FIELD_KIND_BOOLEAN;
638 0           ret.value.value.boolean = value;
639 0           return ret;
640             }
641              
642 0           amqp_table_entry_t *amqp_table_get_entry_by_key(const amqp_table_t *table,
643             const amqp_bytes_t key) {
644             int i;
645 0 0         assert(table != NULL);
646 0 0         for (i = 0; i < table->num_entries; ++i) {
647 0 0         if (amqp_bytes_equal(table->entries[i].key, key)) {
648 0           return &table->entries[i];
649             }
650             }
651 0           return NULL;
652             }