2 * Wireshark CBOR item decoding API.
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
16 #include <wsutil/array.h>
17 #include <epan/exceptions.h>
18 #include <epan/expert.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
41 /// The start offset of this header
43 /// The length of just this header
45 /// The expert info object (if error)
48 /// Major type of this item (cbor_type)
50 /// Minor type of this item
52 /// Raw head "value" which may be from the @c type_minor
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
) {
63 head
->rawvalue
= tvb_get_uint8(tvb
, head
->start
+ head
->length
);
67 head
->rawvalue
= tvb_get_uint16(tvb
, head
->start
+ head
->length
, ENC_BIG_ENDIAN
);
71 head
->rawvalue
= tvb_get_uint32(tvb
, head
->start
+ head
->length
, ENC_BIG_ENDIAN
);
75 head
->rawvalue
= tvb_get_uint64(tvb
, head
->start
+ head
->length
, ENC_BIG_ENDIAN
);
79 if (head
->type_minor
<= 0x17) {
80 head
->rawvalue
= head
->type_minor
;
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
);
102 // Match libcbor enums
103 head
->type_major
= (first
& 0xe0) >> 5;
104 head
->type_minor
= (first
& 0x1f);
105 switch ((cbor_type
)(head
->type_major
)) {
107 case CBOR_TYPE_NEGINT
:
109 wscbor_read_unsigned(head
, tvb
);
110 if (head
->type_minor
> 0x1B) {
111 head
->error
= &ei_cbor_invalid
;
114 case CBOR_TYPE_BYTESTRING
:
115 case CBOR_TYPE_STRING
:
116 case CBOR_TYPE_ARRAY
:
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
;
126 head
->error
= &ei_cbor_invalid
;
130 *offset
+= head
->length
;
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*)
145 /// For string types, including indefinite length, the item payload.
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
) {
157 if (head_value
> INT_MAX
) {
158 wmem_list_append(chunk
->errors
, wscbor_error_new(
159 chunk
->_priv
->alloc
, &ei_cbor_overflow
,
165 length
= (int) head_value
;
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
);
174 wmem_strbuf_t
*buf
= wmem_strbuf_new(alloc
, "");
177 va_start(ap
, format
);
178 wmem_strbuf_append_vprintf(buf
, format
, ap
);
181 err
->msg
= wmem_strbuf_finalize(buf
);
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
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
;
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
);
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
);
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
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
);
241 // indefinite length, sequence of definite items
242 chunk
->_priv
->str_value
= NULL
;
245 wscbor_head_t
*head
= wscbor_head_read(alloc
, tvb
, offset
);
246 chunk
->data_length
+= head
->length
;
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)
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
263 const int datalen
= wscbor_get_length(chunk
, head
->rawvalue
);
265 chunk
->data_length
+= 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
);
284 wmem_list_append(chunk
->_priv
->infos
, wscbor_error_new(
285 chunk
->_priv
->alloc
, &ei_cbor_indef_string
,
289 if (chunk
->_priv
->str_value
) {
290 tvb_composite_finalize(chunk
->_priv
->str_value
);
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);
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()
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
;
334 expert_add_info_format(ctx
->pinfo
, ctx
->item
, err
->ei
, "%s", err
->msg
);
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
) {
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
);
374 switch (chunk
->type_major
) {
376 case CBOR_TYPE_NEGINT
:
378 case CBOR_TYPE_FLOAT_CTRL
:
380 case CBOR_TYPE_BYTESTRING
:
381 case CBOR_TYPE_STRING
:
382 // wscbor_read_chunk() sets offset past string value
384 case CBOR_TYPE_ARRAY
: {
385 if (chunk
->type_minor
== 31) {
386 // wait for indefinite break
387 bool was_break
= false;
389 if (!wscbor_skip_next_item_internal(alloc
, tvb
, offset
, &was_break
)) {
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
)) {
405 case CBOR_TYPE_MAP
: {
406 if (chunk
->type_minor
== 31) {
407 // wait for indefinite break
408 bool was_break
= false;
410 if (!wscbor_skip_next_item_internal(alloc
, tvb
, offset
, &was_break
)) {
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
)) {
422 if (!wscbor_skip_next_item_internal(alloc
, tvb
, offset
, NULL
)) {
430 const bool got_break
= wscbor_is_indefinite_break(chunk
);
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) {
449 *offset
= chunk
->start
;
450 wscbor_skip_next_item(alloc
, tvb
, offset
);
454 void wscbor_init(void) {
455 proto_wscbor
= proto_register_protocol(
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
) {
471 *size
= array_length(expertitems
);
476 bool wscbor_require_major_type(wscbor_chunk_t
*chunk
, cbor_type major
) {
477 if (chunk
->type_major
== major
) {
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
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
)) {
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
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
)) {
516 switch (chunk
->type_minor
) {
518 case CBOR_CTRL_FALSE
: {
520 value
= wmem_new(alloc
, bool);
521 *value
= (chunk
->type_minor
== CBOR_CTRL_TRUE
);
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
535 uint64_t * wscbor_require_uint64(wmem_allocator_t
*alloc
, wscbor_chunk_t
*chunk
) {
536 if (!wscbor_require_major_type(chunk
, CBOR_TYPE_UINT
)) {
540 uint64_t *result
= wmem_new(alloc
, uint64_t);
541 *result
= chunk
->head_value
;
545 int64_t * wscbor_require_int64(wmem_allocator_t
*alloc
, wscbor_chunk_t
*chunk
) {
546 int64_t *result
= NULL
;
547 switch (chunk
->type_major
) {
549 case CBOR_TYPE_NEGINT
: {
551 if (chunk
->head_value
> INT64_MAX
) {
553 wmem_list_append(chunk
->errors
, wscbor_error_new(
554 chunk
->_priv
->alloc
, &ei_cbor_overflow
,
559 clamped
= chunk
->head_value
;
562 result
= wmem_new(alloc
, int64_t);
563 if (chunk
->type_major
== CBOR_TYPE_NEGINT
) {
564 *result
= -clamped
- 1;
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
582 char * wscbor_require_tstr(wmem_allocator_t
*alloc
, wscbor_chunk_t
*chunk
) {
583 if (!wscbor_require_major_type(chunk
, CBOR_TYPE_STRING
)) {
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
)) {
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
);
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
);
608 item
= proto_tree_add_item(tree
, hfindex
, tvb
, chunk
->start
, -1, 0);
610 wscbor_chunk_mark_errors(pinfo
, item
, chunk
);
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
);
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
);
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
);
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
);
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
);
641 switch (field
->type
) {
655 fprintf(stderr
, "Unhandled bitmask size: %d", field
->type
);
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;
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
);
675 proto_item
* proto_tree_add_cbor_tstr(proto_tree
*tree
, int hfindex
, packet_info
*pinfo
, tvbuff_t
*tvb
, const wscbor_chunk_t
*chunk
) {
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
);
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
);
688 proto_item
* proto_tree_add_cbor_bstr(proto_tree
*tree
, int hfindex
, packet_info
*pinfo
, tvbuff_t
*tvb
, const wscbor_chunk_t
*chunk
) {
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);
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
);
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
);