update epan/dissectors/pidl/drsuapi/drsuapi.idl from samba
[wireshark-sm.git] / epan / wscbor.c
blob2e2873c590064be73200e208980280db82e7a872
1 /* wscbor.c
2 * Wireshark CBOR item decoding API.
3 * References:
4 * RFC 8949: https://tools.ietf.org/html/rfc8949
6 * Copyright 2019-2021, Brian Sipos <brian.sipos@gmail.com>
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * SPDX-License-Identifier: LGPL-2.1-or-later
14 #include "config.h"
16 #include <wsutil/array.h>
17 #include <epan/exceptions.h>
18 #include <epan/expert.h>
19 #include <stdio.h>
20 #include <inttypes.h>
21 #include "wscbor.h"
23 /// Pseudo-protocol to register expert info
24 static int proto_wscbor;
26 static expert_field ei_cbor_invalid;
27 static expert_field ei_cbor_overflow;
28 static expert_field ei_cbor_wrong_type;
29 static expert_field ei_cbor_array_wrong_size;
30 static expert_field ei_cbor_indef_string;
31 static ei_register_info expertitems[] = {
32 {&ei_cbor_invalid, {"_ws.wscbor.cbor_invalid", PI_MALFORMED, PI_ERROR, "CBOR cannot be decoded", EXPFILL}},
33 {&ei_cbor_overflow, {"_ws.wscbor.cbor_overflow", PI_UNDECODED, PI_ERROR, "CBOR overflow of Wireshark value", EXPFILL}},
34 {&ei_cbor_wrong_type, {"_ws.wscbor.cbor_wrong_type", PI_MALFORMED, PI_ERROR, "CBOR is wrong type", EXPFILL}},
35 {&ei_cbor_array_wrong_size, {"_ws.wscbor.array_wrong_size", PI_MALFORMED, PI_WARN, "CBOR array is the wrong size", EXPFILL}},
36 {&ei_cbor_indef_string, {"_ws.wscbor.indef_string", PI_COMMENTS_GROUP, PI_COMMENT, "String uses indefinite-length encoding", EXPFILL}},
39 /// The basic header structure of CBOR encoding
40 typedef struct {
41 /// The start offset of this header
42 int start;
43 /// The length of just this header
44 int length;
45 /// The expert info object (if error)
46 expert_field *error;
48 /// Major type of this item (cbor_type)
49 uint8_t type_major;
50 /// Minor type of this item
51 uint8_t type_minor;
52 /// Raw head "value" which may be from the @c type_minor
53 uint64_t rawvalue;
54 } wscbor_head_t;
56 /** Read the raw value from a CBOR head.
57 * @param[in,out] head The head to read into.
58 * @param tvb The buffer to read from.
60 static void wscbor_read_unsigned(wscbor_head_t *head, tvbuff_t *tvb) {
61 switch (head->type_minor) {
62 case 0x18:
63 head->rawvalue = tvb_get_uint8(tvb, head->start + head->length);
64 head->length += 1;
65 break;
66 case 0x19:
67 head->rawvalue = tvb_get_uint16(tvb, head->start + head->length, ENC_BIG_ENDIAN);
68 head->length += 2;
69 break;
70 case 0x1A:
71 head->rawvalue = tvb_get_uint32(tvb, head->start + head->length, ENC_BIG_ENDIAN);
72 head->length += 4;
73 break;
74 case 0x1B:
75 head->rawvalue = tvb_get_uint64(tvb, head->start + head->length, ENC_BIG_ENDIAN);
76 head->length += 8;
77 break;
78 default:
79 if (head->type_minor <= 0x17) {
80 head->rawvalue = head->type_minor;
82 break;
86 /** Read just the CBOR head octet.
87 * @param alloc The allocator to use.
88 * @param tvb The TVB to read from.
89 * @param[in,out] offset The offset with in @c tvb.
90 * This is updated with just the head length.
91 * @return The new head object.
92 * This never returns NULL.
93 * @post Will throw wireshark exception if read fails.
95 static wscbor_head_t * wscbor_head_read(wmem_allocator_t *alloc, tvbuff_t *tvb, int *offset) {
96 wscbor_head_t *head = wmem_new0(alloc, wscbor_head_t);
98 head->start = *offset;
99 const uint8_t first = tvb_get_uint8(tvb, head->start);
100 head->length += 1;
102 // Match libcbor enums
103 head->type_major = (first & 0xe0) >> 5;
104 head->type_minor = (first & 0x1f);
105 switch ((cbor_type)(head->type_major)) {
106 case CBOR_TYPE_UINT:
107 case CBOR_TYPE_NEGINT:
108 case CBOR_TYPE_TAG:
109 wscbor_read_unsigned(head, tvb);
110 if (head->type_minor > 0x1B) {
111 head->error = &ei_cbor_invalid;
113 break;
114 case CBOR_TYPE_BYTESTRING:
115 case CBOR_TYPE_STRING:
116 case CBOR_TYPE_ARRAY:
117 case CBOR_TYPE_MAP:
118 case CBOR_TYPE_FLOAT_CTRL:
119 wscbor_read_unsigned(head, tvb);
120 if ((head->type_minor > 0x1B) && (head->type_minor < 0x1F)) {
121 head->error = &ei_cbor_invalid;
123 break;
125 default:
126 head->error = &ei_cbor_invalid;
127 break;
130 *offset += head->length;
131 return head;
134 /** Force a head to be freed.
136 static void wscbor_head_free(wmem_allocator_t *alloc, wscbor_head_t *head) {
137 wmem_free(alloc, head);
140 struct _wscbor_chunk_priv_t {
141 /// The allocator used for wscbor_chunk_t.errors and wscbor_chunk_t.tags
142 wmem_allocator_t *alloc;
143 /// Non-error expert info on this chunk (type wscbor_error_t*)
144 wmem_list_t *infos;
145 /// For string types, including indefinite length, the item payload.
146 /// Otherwise NULL.
147 tvbuff_t *str_value;
150 /** Get a clamped string length suitable for tvb functions.
151 * @param[in,out] chunk The chunk to set errors on.
152 * @param head_value The value to clamp.
153 * @return The clamped length value.
155 static int wscbor_get_length(wscbor_chunk_t *chunk, uint64_t head_value) {
156 int length;
157 if (head_value > INT_MAX) {
158 wmem_list_append(chunk->errors, wscbor_error_new(
159 chunk->_priv->alloc, &ei_cbor_overflow,
160 NULL
162 length = INT_MAX;
164 else {
165 length = (int) head_value;
167 return length;
170 wscbor_error_t * wscbor_error_new(wmem_allocator_t *alloc, expert_field *ei, const char *format, ...) {
171 wscbor_error_t *err = wmem_new0(alloc, wscbor_error_t);
172 err->ei = ei;
173 if (format) {
174 wmem_strbuf_t *buf = wmem_strbuf_new(alloc, "");
176 va_list ap;
177 va_start(ap, format);
178 wmem_strbuf_append_vprintf(buf, format, ap);
179 va_end(ap);
181 err->msg = wmem_strbuf_finalize(buf);
183 return err;
186 wscbor_chunk_t * wscbor_chunk_read(wmem_allocator_t *alloc, tvbuff_t *tvb, int *offset) {
187 DISSECTOR_ASSERT(alloc != NULL);
188 DISSECTOR_ASSERT(offset != NULL);
189 DISSECTOR_ASSERT(tvb != NULL);
191 wscbor_chunk_t *chunk = wmem_new0(alloc, wscbor_chunk_t);
192 chunk->_priv = wmem_new0(alloc, struct _wscbor_chunk_priv_t);
193 chunk->_priv->alloc = alloc;
194 chunk->_priv->infos = wmem_list_new(alloc);
195 chunk->errors = wmem_list_new(alloc);
196 chunk->tags = wmem_list_new(alloc);
197 chunk->start = *offset;
199 // Read a sequence of tags followed by an item header
200 while (true) {
201 // This will break out of the loop if it runs out of buffer
202 wscbor_head_t *head = wscbor_head_read(alloc, tvb, offset);
203 chunk->head_length += head->length;
204 if (head->error) {
205 wmem_list_append(chunk->errors, wscbor_error_new(alloc, head->error, NULL));
207 if (head->type_major == CBOR_TYPE_TAG) {
208 wscbor_tag_t *tag = wmem_new(alloc, wscbor_tag_t);
209 tag->start = head->start;
210 tag->length = head->length;
211 tag->value = head->rawvalue;
212 wmem_list_append(chunk->tags, tag);
213 // same chunk, next part
214 wscbor_head_free(alloc, head);
215 continue;
218 // An actual (non-tag) header
219 chunk->type_major = (cbor_type)head->type_major;
220 chunk->type_minor = head->type_minor;
221 chunk->head_value = head->rawvalue;
223 wscbor_head_free(alloc, head);
224 break;
227 // Data beyond the tags and item head
228 chunk->data_length = chunk->head_length;
229 switch (chunk->type_major) {
230 case CBOR_TYPE_BYTESTRING:
231 case CBOR_TYPE_STRING:
232 if (chunk->type_minor != 31) {
233 const int datalen = wscbor_get_length(chunk, chunk->head_value);
234 // skip over definite data
235 *offset += datalen;
236 chunk->data_length += datalen;
237 // allow even zero-length strings
238 chunk->_priv->str_value = tvb_new_subset_length(tvb, chunk->start + chunk->head_length, datalen);
240 else {
241 // indefinite length, sequence of definite items
242 chunk->_priv->str_value = NULL;
244 while (true) {
245 wscbor_head_t *head = wscbor_head_read(alloc, tvb, offset);
246 chunk->data_length += head->length;
247 if (head->error) {
248 wmem_list_append(chunk->errors, wscbor_error_new(alloc, head->error, NULL));
250 const bool is_break = (
251 (head->type_major == CBOR_TYPE_FLOAT_CTRL)
252 && (head->type_minor == 31)
254 if (!is_break) {
255 if (head->type_major != chunk->type_major) {
256 wmem_list_append(chunk->errors, wscbor_error_new(
257 chunk->_priv->alloc, &ei_cbor_wrong_type,
258 "Indefinite sub-string item has major type %d, should be %d",
259 head->type_major, chunk->type_major
262 else {
263 const int datalen = wscbor_get_length(chunk, head->rawvalue);
264 *offset += datalen;
265 chunk->data_length += datalen;
266 if(datalen) {
267 if (!chunk->_priv->str_value) {
268 chunk->_priv->str_value = tvb_new_composite ();
270 tvb_composite_append(
271 chunk->_priv->str_value,
272 tvb_new_subset_length(tvb, head->start + head->length, datalen)
278 wscbor_head_free(alloc, head);
279 if (is_break) {
280 break;
284 wmem_list_append(chunk->_priv->infos, wscbor_error_new(
285 chunk->_priv->alloc, &ei_cbor_indef_string,
286 NULL
289 if (chunk->_priv->str_value) {
290 tvb_composite_finalize(chunk->_priv->str_value);
292 else {
293 // Create an empty subset tvb. str_value is expected to be non-NULL for string types.
294 chunk->_priv->str_value = tvb_new_subset_length (tvb, 0, 0);
297 break;
298 default:
299 break;
302 return chunk;
305 static void wscbor_subitem_free(void *data, void *userdata) {
306 wmem_allocator_t *alloc = (wmem_allocator_t *) userdata;
307 wmem_free(alloc, data);
310 void wscbor_chunk_free(wscbor_chunk_t *chunk) {
311 DISSECTOR_ASSERT(chunk);
312 wmem_allocator_t *alloc = chunk->_priv->alloc;
313 wmem_list_foreach(chunk->_priv->infos, wscbor_subitem_free, alloc);
314 wmem_destroy_list(chunk->_priv->infos);
315 wmem_list_foreach(chunk->errors, wscbor_subitem_free, alloc);
316 wmem_destroy_list(chunk->errors);
317 wmem_list_foreach(chunk->tags, wscbor_subitem_free, alloc);
318 wmem_destroy_list(chunk->tags);
319 wmem_free(alloc, chunk);
322 /// User data for wscbor_expert_add()
323 typedef struct {
324 packet_info *pinfo;
325 proto_item *item;
326 } wscbor_expert_add_t;
328 /// A callback for wmem_list_foreach() to add the info
329 static void wscbor_expert_add(void *data, void *userdata) {
330 const wscbor_error_t *err = (const wscbor_error_t *)data;
331 wscbor_expert_add_t *ctx = (wscbor_expert_add_t *)userdata;
333 if (err->msg) {
334 expert_add_info_format(ctx->pinfo, ctx->item, err->ei, "%s", err->msg);
336 else {
337 expert_add_info(ctx->pinfo, ctx->item, err->ei);
341 uint64_t wscbor_chunk_mark_errors(packet_info *pinfo, proto_item *item, const wscbor_chunk_t *chunk) {
342 wscbor_expert_add_t ctx = {pinfo, item};
343 wmem_list_foreach(chunk->errors, wscbor_expert_add, &ctx);
344 wmem_list_foreach(chunk->_priv->infos, wscbor_expert_add, &ctx);
346 return wmem_list_count(chunk->errors);
349 unsigned wscbor_has_errors(const wscbor_chunk_t *chunk) {
350 return wmem_list_count(chunk->errors);
353 bool wscbor_is_indefinite_break(const wscbor_chunk_t *chunk) {
354 return (
355 (chunk->type_major == CBOR_TYPE_FLOAT_CTRL)
356 && (chunk->type_minor == 31)
360 /** Add output parameter to indicate internal state.
361 * @param alloc The allocator to use.
362 * @param tvb The data buffer.
363 * @param[in,out] offset The initial offset to read and skip over.
364 * @param[out] is_break If non-null, set to true only when the item was
365 * an indefinite break.
366 * @return True if the skipped item was fully valid.
368 static bool wscbor_skip_next_item_internal(wmem_allocator_t *alloc, tvbuff_t *tvb, int *offset, bool *is_break) {
369 wscbor_chunk_t *chunk = wscbor_chunk_read(alloc, tvb, offset);
370 if (wscbor_has_errors(chunk)) {
371 wscbor_chunk_free(chunk);
372 return false;
374 switch (chunk->type_major) {
375 case CBOR_TYPE_UINT:
376 case CBOR_TYPE_NEGINT:
377 case CBOR_TYPE_TAG:
378 case CBOR_TYPE_FLOAT_CTRL:
379 break;
380 case CBOR_TYPE_BYTESTRING:
381 case CBOR_TYPE_STRING:
382 // wscbor_read_chunk() sets offset past string value
383 break;
384 case CBOR_TYPE_ARRAY: {
385 if (chunk->type_minor == 31) {
386 // wait for indefinite break
387 bool was_break = false;
388 do {
389 if (!wscbor_skip_next_item_internal(alloc, tvb, offset, &was_break)) {
390 return false;
393 while (!was_break);
395 else {
396 const uint64_t count = chunk->head_value;
397 for (uint64_t ix = 0; ix < count; ++ix) {
398 if (!wscbor_skip_next_item_internal(alloc, tvb, offset, NULL)) {
399 return false;
403 break;
405 case CBOR_TYPE_MAP: {
406 if (chunk->type_minor == 31) {
407 // wait for indefinite break
408 bool was_break = false;
409 do {
410 if (!wscbor_skip_next_item_internal(alloc, tvb, offset, &was_break)) {
411 return false;
414 while (!was_break);
416 else {
417 const uint64_t count = chunk->head_value;
418 for (uint64_t ix = 0; ix < count; ++ix) {
419 if (!wscbor_skip_next_item_internal(alloc, tvb, offset, NULL)) {
420 return false;
422 if (!wscbor_skip_next_item_internal(alloc, tvb, offset, NULL)) {
423 return false;
427 break;
430 const bool got_break = wscbor_is_indefinite_break(chunk);
431 if (is_break) {
432 *is_break = got_break;
434 wscbor_chunk_free(chunk);
435 // RFC 8949 Sec 3.2.1: a break code outside of an indefinite container is
436 // not valid, and is_break is non-null only in indefinite container.
437 return is_break || !got_break;
440 bool wscbor_skip_next_item(wmem_allocator_t *alloc, tvbuff_t *tvb, int *offset) {
441 return wscbor_skip_next_item_internal(alloc, tvb, offset, NULL);
444 bool wscbor_skip_if_errors(wmem_allocator_t *alloc, tvbuff_t *tvb, int *offset, const wscbor_chunk_t *chunk) {
445 if (wscbor_has_errors(chunk) == 0) {
446 return false;
449 *offset = chunk->start;
450 wscbor_skip_next_item(alloc, tvb, offset);
451 return true;
454 void wscbor_init(void) {
455 proto_wscbor = proto_register_protocol(
456 "CBOR Item Decoder",
457 "CBOR Item Decoder",
458 "_ws.wscbor"
461 expert_module_t *expert_wscbor = expert_register_protocol(proto_wscbor);
462 /* This isn't really a protocol, it's an error indication;
463 disabling them makes no sense. */
464 proto_set_cant_toggle(proto_wscbor);
466 expert_register_field_array(expert_wscbor, expertitems, array_length(expertitems));
469 const ei_register_info * wscbor_expert_items(int *size) {
470 if (size) {
471 *size = array_length(expertitems);
473 return expertitems;
476 bool wscbor_require_major_type(wscbor_chunk_t *chunk, cbor_type major) {
477 if (chunk->type_major == major) {
478 return true;
480 wmem_list_append(chunk->errors, wscbor_error_new(
481 chunk->_priv->alloc, &ei_cbor_wrong_type,
482 "Item has major type %d, should be %d",
483 chunk->type_major, major
485 return false;
488 bool wscbor_require_array(wscbor_chunk_t *chunk) {
489 return wscbor_require_major_type(chunk, CBOR_TYPE_ARRAY);
492 bool wscbor_require_array_size(wscbor_chunk_t *chunk, uint64_t count_min, uint64_t count_max) {
493 if (!wscbor_require_array(chunk)) {
494 return false;
496 if ((chunk->head_value < count_min) || (chunk->head_value > count_max)) {
497 wmem_list_append(chunk->errors, wscbor_error_new(
498 chunk->_priv->alloc, &ei_cbor_array_wrong_size,
499 "Array has %" PRId64 " items, should be within [%"PRId64", %"PRId64"]",
500 chunk->head_value, count_min, count_max
502 return false;
504 return true;
507 bool wscbor_require_map(wscbor_chunk_t *chunk) {
508 return wscbor_require_major_type(chunk, CBOR_TYPE_MAP);
511 bool * wscbor_require_boolean(wmem_allocator_t *alloc, wscbor_chunk_t *chunk) {
512 if (!wscbor_require_major_type(chunk, CBOR_TYPE_FLOAT_CTRL)) {
513 return NULL;
516 switch (chunk->type_minor) {
517 case CBOR_CTRL_TRUE:
518 case CBOR_CTRL_FALSE: {
519 bool *value = NULL;
520 value = wmem_new(alloc, bool);
521 *value = (chunk->type_minor == CBOR_CTRL_TRUE);
522 return value;
524 default:
525 wmem_list_append(chunk->errors, wscbor_error_new(
526 chunk->_priv->alloc, &ei_cbor_wrong_type,
527 "Item has minor type %d, should be %d or %d",
528 chunk->type_minor, CBOR_CTRL_TRUE, CBOR_CTRL_FALSE
530 break;
532 return NULL;
535 uint64_t * wscbor_require_uint64(wmem_allocator_t *alloc, wscbor_chunk_t *chunk) {
536 if (!wscbor_require_major_type(chunk, CBOR_TYPE_UINT)) {
537 return NULL;
540 uint64_t *result = wmem_new(alloc, uint64_t);
541 *result = chunk->head_value;
542 return result;
545 int64_t * wscbor_require_int64(wmem_allocator_t *alloc, wscbor_chunk_t *chunk) {
546 int64_t *result = NULL;
547 switch (chunk->type_major) {
548 case CBOR_TYPE_UINT:
549 case CBOR_TYPE_NEGINT: {
550 int64_t clamped;
551 if (chunk->head_value > INT64_MAX) {
552 clamped = INT64_MAX;
553 wmem_list_append(chunk->errors, wscbor_error_new(
554 chunk->_priv->alloc, &ei_cbor_overflow,
555 NULL
558 else {
559 clamped = chunk->head_value;
562 result = wmem_new(alloc, int64_t);
563 if (chunk->type_major == CBOR_TYPE_NEGINT) {
564 *result = -clamped - 1;
566 else {
567 *result = clamped;
569 break;
571 default:
572 wmem_list_append(chunk->errors, wscbor_error_new(
573 chunk->_priv->alloc, &ei_cbor_wrong_type,
574 "Item has major type %d, should be %d or %d",
575 chunk->type_major, CBOR_TYPE_UINT, CBOR_TYPE_NEGINT
577 break;
579 return result;
582 char * wscbor_require_tstr(wmem_allocator_t *alloc, wscbor_chunk_t *chunk) {
583 if (!wscbor_require_major_type(chunk, CBOR_TYPE_STRING)) {
584 return NULL;
587 return (char *)tvb_get_string_enc(alloc, chunk->_priv->str_value, 0, tvb_reported_length(chunk->_priv->str_value), ENC_UTF_8);
590 tvbuff_t * wscbor_require_bstr(wmem_allocator_t *alloc _U_, wscbor_chunk_t *chunk) {
591 if (!wscbor_require_major_type(chunk, CBOR_TYPE_BYTESTRING)) {
592 return NULL;
595 return chunk->_priv->str_value;
598 proto_item * proto_tree_add_cbor_container(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk) {
599 const header_field_info *hfinfo = proto_registrar_get_nth(hfindex);
600 proto_item *item;
601 if (FT_IS_UINT(hfinfo->type)) {
602 item = proto_tree_add_uint64(tree, hfindex, tvb, chunk->start, chunk->head_length, chunk->head_value);
604 else if (FT_IS_INT(hfinfo->type)) {
605 item = proto_tree_add_int64(tree, hfindex, tvb, chunk->start, chunk->head_length, chunk->head_value);
607 else {
608 item = proto_tree_add_item(tree, hfindex, tvb, chunk->start, -1, 0);
610 wscbor_chunk_mark_errors(pinfo, item, chunk);
611 return item;
614 proto_item * proto_tree_add_cbor_ctrl(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk) {
615 proto_item *item = proto_tree_add_item(tree, hfindex, tvb, chunk->start, chunk->head_length, 0);
616 wscbor_chunk_mark_errors(pinfo, item, chunk);
617 return item;
620 proto_item * proto_tree_add_cbor_boolean(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk, const bool *value) {
621 proto_item *item = proto_tree_add_boolean(tree, hfindex, tvb, chunk->start, chunk->data_length, value ? *value : false);
622 wscbor_chunk_mark_errors(pinfo, item, chunk);
623 return item;
626 proto_item * proto_tree_add_cbor_uint64(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk, const uint64_t *value) {
627 proto_item *item = proto_tree_add_uint64(tree, hfindex, tvb, chunk->start, chunk->head_length, value ? *value : 0);
628 wscbor_chunk_mark_errors(pinfo, item, chunk);
629 return item;
632 proto_item * proto_tree_add_cbor_int64(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk, const int64_t *value) {
633 proto_item *item = proto_tree_add_int64(tree, hfindex, tvb, chunk->start, chunk->head_length, value ? *value : 0);
634 wscbor_chunk_mark_errors(pinfo, item, chunk);
635 return item;
638 proto_item * proto_tree_add_cbor_bitmask(proto_tree *tree, int hfindex, const int ett, int *const *fields, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk, const uint64_t *value) {
639 header_field_info *field = proto_registrar_get_nth(hfindex);
640 int flagsize = 0;
641 switch (field->type) {
642 case FT_UINT8:
643 flagsize = 1;
644 break;
645 case FT_UINT16:
646 flagsize = 2;
647 break;
648 case FT_UINT32:
649 flagsize = 4;
650 break;
651 case FT_UINT64:
652 flagsize = 8;
653 break;
654 default:
655 fprintf(stderr, "Unhandled bitmask size: %d", field->type);
656 return NULL;
659 // Fake TVB data for these functions
660 uint8_t *flags = (uint8_t *) wmem_alloc0(pinfo->pool, flagsize);
661 { // Inject big-endian value directly
662 uint64_t buf = (value ? *value : 0);
663 for (int ix = flagsize - 1; ix >= 0; --ix) {
664 flags[ix] = buf & 0xFF;
665 buf >>= 8;
668 tvbuff_t *tvb_flags = tvb_new_child_real_data(tvb, flags, flagsize, flagsize);
670 proto_item *item = proto_tree_add_bitmask_value(tree, tvb_flags, 0, hfindex, ett, fields, value ? *value : 0);
671 wscbor_chunk_mark_errors(pinfo, item, chunk);
672 return item;
675 proto_item * proto_tree_add_cbor_tstr(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk) {
676 proto_item *item;
677 if (chunk->_priv->str_value) {
678 item = proto_tree_add_item(tree, hfindex, chunk->_priv->str_value, 0, tvb_reported_length(chunk->_priv->str_value), ENC_UTF_8);
680 else {
681 // still show an empty item with errors
682 item = proto_tree_add_item(tree, hfindex, tvb, chunk->start, 0, 0);
684 wscbor_chunk_mark_errors(pinfo, item, chunk);
685 return item;
688 proto_item * proto_tree_add_cbor_bstr(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk) {
689 proto_item *item;
690 if (chunk->_priv->str_value) {
691 item = proto_tree_add_item(tree, hfindex, chunk->_priv->str_value, 0, tvb_reported_length(chunk->_priv->str_value), 0);
693 else {
694 // still show an empty item with errors
695 item = proto_tree_add_item(tree, hfindex, tvb, chunk->start, 0, 0);
697 wscbor_chunk_mark_errors(pinfo, item, chunk);
698 return item;
701 proto_item * proto_tree_add_cbor_strlen(proto_tree *tree, int hfindex, packet_info *pinfo _U_, tvbuff_t *tvb, const wscbor_chunk_t *chunk) {
702 const unsigned str_len = (chunk->_priv->str_value ? tvb_reported_length(chunk->_priv->str_value) : 0);
703 proto_item *item = proto_tree_add_uint64(tree, hfindex, tvb, chunk->start, chunk->head_length, str_len);
704 return item;