/src/wireshark/epan/wscbor.c
Line | Count | Source |
1 | | /* wscbor.c |
2 | | * Wireshark CBOR item decoding API. |
3 | | * References: |
4 | | * RFC 8949: https://tools.ietf.org/html/rfc8949 |
5 | | * |
6 | | * Copyright 2019-2021, Brian Sipos <brian.sipos@gmail.com> |
7 | | * |
8 | | * Wireshark - Network traffic analyzer |
9 | | * By Gerald Combs <gerald@wireshark.org> |
10 | | * Copyright 1998 Gerald Combs |
11 | | * |
12 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
13 | | */ |
14 | | #include "config.h" |
15 | | |
16 | | #include <wsutil/array.h> |
17 | | #include <epan/exceptions.h> |
18 | | #include <epan/expert.h> |
19 | | #include <epan/prefs.h> |
20 | | #include <stdio.h> |
21 | | #include <inttypes.h> |
22 | | #include "wscbor.h" |
23 | | |
24 | | /// Pseudo-protocol to register expert info |
25 | | static int proto_wscbor; |
26 | | |
27 | | static expert_field ei_cbor_invalid; |
28 | | static expert_field ei_cbor_overflow; |
29 | | static expert_field ei_cbor_wrong_type; |
30 | | static expert_field ei_cbor_array_wrong_size; |
31 | | static expert_field ei_cbor_indef_string; |
32 | | static ei_register_info expertitems[] = { |
33 | | {&ei_cbor_invalid, {"_ws.wscbor.cbor_invalid", PI_MALFORMED, PI_ERROR, "CBOR cannot be decoded", EXPFILL}}, |
34 | | {&ei_cbor_overflow, {"_ws.wscbor.cbor_overflow", PI_UNDECODED, PI_ERROR, "CBOR overflow of Wireshark value", EXPFILL}}, |
35 | | {&ei_cbor_wrong_type, {"_ws.wscbor.cbor_wrong_type", PI_MALFORMED, PI_ERROR, "CBOR is wrong type", EXPFILL}}, |
36 | | {&ei_cbor_array_wrong_size, {"_ws.wscbor.array_wrong_size", PI_MALFORMED, PI_WARN, "CBOR array is the wrong size", EXPFILL}}, |
37 | | {&ei_cbor_indef_string, {"_ws.wscbor.indef_string", PI_COMMENTS_GROUP, PI_COMMENT, "String uses indefinite-length encoding", EXPFILL}}, |
38 | | }; |
39 | | |
40 | | /// The basic header structure of CBOR encoding |
41 | | typedef struct { |
42 | | /// The start offset of this header |
43 | | int start; |
44 | | /// The length of just this header |
45 | | int length; |
46 | | /// The expert info object (if error) |
47 | | expert_field *error; |
48 | | |
49 | | /// Major type of this item (cbor_type) |
50 | | uint8_t type_major; |
51 | | /// Minor type of this item |
52 | | uint8_t type_minor; |
53 | | /// Raw head "value" which may be from the @c type_minor |
54 | | uint64_t rawvalue; |
55 | | } wscbor_head_t; |
56 | | |
57 | | /** Read the raw value from a CBOR head. |
58 | | * @param[in,out] head The head to read into. |
59 | | * @param tvb The buffer to read from. |
60 | | */ |
61 | 334k | static void wscbor_read_unsigned(wscbor_head_t *head, tvbuff_t *tvb) { |
62 | 334k | switch (head->type_minor) { |
63 | 5.57k | case 0x18: |
64 | 5.57k | head->rawvalue = tvb_get_uint8(tvb, head->start + head->length); |
65 | 5.57k | head->length += 1; |
66 | 5.57k | break; |
67 | 3.34k | case 0x19: |
68 | 3.34k | head->rawvalue = tvb_get_uint16(tvb, head->start + head->length, ENC_BIG_ENDIAN); |
69 | 3.34k | head->length += 2; |
70 | 3.34k | break; |
71 | 2.26k | case 0x1A: |
72 | 2.26k | head->rawvalue = tvb_get_uint32(tvb, head->start + head->length, ENC_BIG_ENDIAN); |
73 | 2.26k | head->length += 4; |
74 | 2.26k | break; |
75 | 2.79k | case 0x1B: |
76 | 2.79k | head->rawvalue = tvb_get_uint64(tvb, head->start + head->length, ENC_BIG_ENDIAN); |
77 | 2.79k | head->length += 8; |
78 | 2.79k | break; |
79 | 320k | default: |
80 | 320k | if (head->type_minor <= 0x17) { |
81 | 272k | head->rawvalue = head->type_minor; |
82 | 272k | } |
83 | 320k | break; |
84 | 334k | } |
85 | 334k | } |
86 | | |
87 | | /** Read just the CBOR head octet. |
88 | | * @param alloc The allocator to use. |
89 | | * @param tvb The TVB to read from. |
90 | | * @param[in,out] offset The offset with in @c tvb. |
91 | | * This is updated with just the head length. |
92 | | * @return The new head object. |
93 | | * This never returns NULL. |
94 | | * @post Will throw wireshark exception if read fails. |
95 | | */ |
96 | 334k | static wscbor_head_t * wscbor_head_read(wmem_allocator_t *alloc, tvbuff_t *tvb, int *offset) { |
97 | 334k | wscbor_head_t *head = wmem_new0(alloc, wscbor_head_t); |
98 | | |
99 | 334k | head->start = *offset; |
100 | 334k | const uint8_t first = tvb_get_uint8(tvb, head->start); |
101 | 334k | head->length += 1; |
102 | | |
103 | | // Match libcbor enums |
104 | 334k | head->type_major = (first & 0xe0) >> 5; |
105 | 334k | head->type_minor = (first & 0x1f); |
106 | 334k | switch ((cbor_type)(head->type_major)) { |
107 | 127k | case CBOR_TYPE_UINT: |
108 | 161k | case CBOR_TYPE_NEGINT: |
109 | 177k | case CBOR_TYPE_TAG: |
110 | 177k | wscbor_read_unsigned(head, tvb); |
111 | 177k | if (head->type_minor > 0x1B) { |
112 | 6.78k | head->error = &ei_cbor_invalid; |
113 | 6.78k | } |
114 | 177k | break; |
115 | 8.89k | case CBOR_TYPE_BYTESTRING: |
116 | 22.8k | case CBOR_TYPE_STRING: |
117 | 81.4k | case CBOR_TYPE_ARRAY: |
118 | 115k | case CBOR_TYPE_MAP: |
119 | 157k | case CBOR_TYPE_FLOAT_CTRL: |
120 | 157k | wscbor_read_unsigned(head, tvb); |
121 | 157k | if ((head->type_minor > 0x1B) && (head->type_minor < 0x1F)) { |
122 | 11.4k | head->error = &ei_cbor_invalid; |
123 | 11.4k | } |
124 | 157k | break; |
125 | | |
126 | 0 | default: |
127 | 0 | head->error = &ei_cbor_invalid; |
128 | 0 | break; |
129 | 334k | } |
130 | | |
131 | 334k | if (ckd_add(offset, *offset, head->length)) { |
132 | 0 | THROW(ReportedBoundsError); |
133 | 0 | } |
134 | 334k | return head; |
135 | 334k | } |
136 | | |
137 | | /** Force a head to be freed. |
138 | | */ |
139 | 334k | static void wscbor_head_free(wmem_allocator_t *alloc, wscbor_head_t *head) { |
140 | 334k | wmem_free(alloc, head); |
141 | 334k | } |
142 | | |
143 | | struct _wscbor_chunk_priv_t { |
144 | | /// The allocator used for wscbor_chunk_t.errors and wscbor_chunk_t.tags |
145 | | wmem_allocator_t *alloc; |
146 | | /// Non-error expert info on this chunk (type wscbor_error_t*) |
147 | | wmem_list_t *infos; |
148 | | /// For string types, including indefinite length, the item payload. |
149 | | /// Otherwise NULL. |
150 | | tvbuff_t *str_value; |
151 | | }; |
152 | | |
153 | | /** Get a clamped string length suitable for tvb functions. |
154 | | * @param[in,out] chunk The chunk to set errors on if the length is clamped. |
155 | | * @param[in,out] offset The current offset to advance to no more than INT_MAX. |
156 | | * @param head_value The value to clamp. |
157 | | * @return The clamped length value. |
158 | | */ |
159 | 21.6k | static int wscbor_get_length(wscbor_chunk_t *chunk, int *offset, uint64_t head_value) { |
160 | 21.6k | int start_offset = *offset; |
161 | 21.6k | int length; |
162 | | /* Truncate the length so that the offset doesn't overflow. |
163 | | * This prevents an exception reading this chunk, but will |
164 | | * throw an exception reading the head of the next chunk |
165 | | * at least when advancing the offset there. |
166 | | */ |
167 | 21.6k | if (ckd_add(offset, start_offset, head_value)) { |
168 | 70 | wmem_list_append(chunk->errors, wscbor_error_new( |
169 | 70 | chunk->_priv->alloc, &ei_cbor_overflow, |
170 | 70 | NULL |
171 | 70 | )); |
172 | 70 | *offset = INT_MAX; |
173 | 70 | length = INT_MAX - start_offset; |
174 | 70 | } |
175 | 21.5k | else { |
176 | 21.5k | length = (int) head_value; |
177 | 21.5k | } |
178 | 21.6k | return length; |
179 | 21.6k | } |
180 | | |
181 | 77.1k | wscbor_error_t * wscbor_error_new(wmem_allocator_t *alloc, expert_field *ei, const char *format, ...) { |
182 | 77.1k | wscbor_error_t *err = wmem_new0(alloc, wscbor_error_t); |
183 | 77.1k | err->ei = ei; |
184 | 77.1k | if (format) { |
185 | 58.2k | wmem_strbuf_t *buf = wmem_strbuf_new(alloc, ""); |
186 | | |
187 | 58.2k | va_list ap; |
188 | 58.2k | va_start(ap, format); |
189 | 58.2k | wmem_strbuf_append_vprintf(buf, format, ap); |
190 | 58.2k | va_end(ap); |
191 | | |
192 | 58.2k | err->msg = wmem_strbuf_finalize(buf); |
193 | 58.2k | } |
194 | 77.1k | return err; |
195 | 77.1k | } |
196 | | |
197 | 305k | wscbor_chunk_t * wscbor_chunk_read(wmem_allocator_t *alloc, tvbuff_t *tvb, int *offset) { |
198 | 305k | DISSECTOR_ASSERT(alloc != NULL); |
199 | 305k | DISSECTOR_ASSERT(offset != NULL); |
200 | 305k | DISSECTOR_ASSERT(tvb != NULL); |
201 | | |
202 | 305k | wscbor_chunk_t *chunk = wmem_new0(alloc, wscbor_chunk_t); |
203 | 305k | chunk->_priv = wmem_new0(alloc, struct _wscbor_chunk_priv_t); |
204 | 305k | chunk->_priv->alloc = alloc; |
205 | 305k | chunk->_priv->infos = wmem_list_new(alloc); |
206 | 305k | chunk->errors = wmem_list_new(alloc); |
207 | 305k | chunk->tags = wmem_list_new(alloc); |
208 | 305k | chunk->start = *offset; |
209 | | |
210 | 305k | CLEANUP_PUSH(((void (*)(void *))wscbor_chunk_free), chunk); |
211 | | |
212 | | // Read a sequence of tags followed by an item header |
213 | 319k | while (true) { |
214 | | // This will break out of the loop if it runs out of buffer |
215 | 319k | wscbor_head_t *head = wscbor_head_read(alloc, tvb, offset); |
216 | 319k | chunk->head_length += head->length; |
217 | 319k | if (head->error) { |
218 | 16.8k | wmem_list_append(chunk->errors, wscbor_error_new(alloc, head->error, NULL)); |
219 | 16.8k | } |
220 | 319k | if (head->type_major == CBOR_TYPE_TAG) { |
221 | 14.4k | wscbor_tag_t *tag = wmem_new(alloc, wscbor_tag_t); |
222 | 14.4k | tag->start = head->start; |
223 | 14.4k | tag->length = head->length; |
224 | 14.4k | tag->value = head->rawvalue; |
225 | 14.4k | wmem_list_append(chunk->tags, tag); |
226 | | // same chunk, next part |
227 | 14.4k | wscbor_head_free(alloc, head); |
228 | 14.4k | continue; |
229 | 14.4k | } |
230 | | |
231 | | // An actual (non-tag) header |
232 | 305k | chunk->type_major = (cbor_type)head->type_major; |
233 | 305k | chunk->type_minor = head->type_minor; |
234 | 305k | chunk->head_value = head->rawvalue; |
235 | | |
236 | 305k | wscbor_head_free(alloc, head); |
237 | 305k | break; |
238 | 319k | } |
239 | | |
240 | | // Data beyond the tags and item head |
241 | 305k | chunk->data_length = chunk->head_length; |
242 | 305k | switch (chunk->type_major) { |
243 | 7.89k | case CBOR_TYPE_BYTESTRING: |
244 | 20.4k | case CBOR_TYPE_STRING: |
245 | 20.4k | if (chunk->type_minor != 31) { |
246 | 19.9k | const int datalen = wscbor_get_length(chunk, offset, chunk->head_value); |
247 | | // skip over definite data |
248 | 19.9k | chunk->data_length += datalen; |
249 | | // allow even zero-length strings |
250 | 19.9k | chunk->_priv->str_value = tvb_new_subset_length(tvb, chunk->start + chunk->head_length, datalen); |
251 | 19.9k | } |
252 | 524 | else { |
253 | | // indefinite length, sequence of definite items |
254 | 524 | chunk->_priv->str_value = NULL; |
255 | | |
256 | 15.1k | while (true) { |
257 | 15.1k | wscbor_head_t *head = wscbor_head_read(alloc, tvb, offset); |
258 | 15.1k | chunk->data_length += head->length; |
259 | 15.1k | if (head->error) { |
260 | 1.39k | wmem_list_append(chunk->errors, wscbor_error_new(alloc, head->error, NULL)); |
261 | 1.39k | } |
262 | 15.1k | const bool is_break = ( |
263 | 15.1k | (head->type_major == CBOR_TYPE_FLOAT_CTRL) |
264 | 2.13k | && (head->type_minor == 31) |
265 | 15.1k | ); |
266 | 15.1k | if (!is_break) { |
267 | 14.6k | if (head->type_major != chunk->type_major) { |
268 | 12.8k | wmem_list_append(chunk->errors, wscbor_error_new( |
269 | 12.8k | chunk->_priv->alloc, &ei_cbor_wrong_type, |
270 | 12.8k | "Indefinite sub-string item has major type %d, should be %d", |
271 | 12.8k | head->type_major, chunk->type_major |
272 | 12.8k | )); |
273 | 12.8k | } |
274 | 1.74k | else { |
275 | 1.74k | const int datalen = wscbor_get_length(chunk, offset, head->rawvalue); |
276 | 1.74k | chunk->data_length += datalen; |
277 | 1.74k | if(datalen) { |
278 | 692 | if (!chunk->_priv->str_value) { |
279 | 222 | chunk->_priv->str_value = tvb_new_composite (); |
280 | 222 | } |
281 | 692 | tvb_composite_append( |
282 | 692 | chunk->_priv->str_value, |
283 | 692 | tvb_new_subset_length(tvb, head->start + head->length, datalen) |
284 | 692 | ); |
285 | 692 | } |
286 | 1.74k | } |
287 | 14.6k | } |
288 | | |
289 | 15.1k | wscbor_head_free(alloc, head); |
290 | 15.1k | if (is_break) { |
291 | 465 | break; |
292 | 465 | } |
293 | 15.1k | } |
294 | | |
295 | 524 | wmem_list_append(chunk->_priv->infos, wscbor_error_new( |
296 | 524 | chunk->_priv->alloc, &ei_cbor_indef_string, |
297 | 524 | NULL |
298 | 524 | )); |
299 | | |
300 | 524 | if (chunk->_priv->str_value) { |
301 | 179 | tvb_composite_finalize(chunk->_priv->str_value); |
302 | 179 | } |
303 | 345 | else { |
304 | | // Create an empty subset tvb. str_value is expected to be non-NULL for string types. |
305 | 345 | chunk->_priv->str_value = tvb_new_subset_length (tvb, 0, 0); |
306 | 345 | } |
307 | 524 | } |
308 | 20.4k | break; |
309 | 284k | default: |
310 | 284k | break; |
311 | 305k | } |
312 | | |
313 | 304k | CLEANUP_POP; |
314 | | |
315 | 304k | return chunk; |
316 | 305k | } |
317 | | |
318 | 25.7k | static void wscbor_subitem_free(void *data, void *userdata) { |
319 | 25.7k | wmem_allocator_t *alloc = (wmem_allocator_t *) userdata; |
320 | 25.7k | wmem_free(alloc, data); |
321 | 25.7k | } |
322 | | |
323 | 127k | void wscbor_chunk_free(wscbor_chunk_t *chunk) { |
324 | 127k | DISSECTOR_ASSERT(chunk); |
325 | 127k | wmem_allocator_t *alloc = chunk->_priv->alloc; |
326 | 127k | wmem_list_foreach(chunk->_priv->infos, wscbor_subitem_free, alloc); |
327 | 127k | wmem_destroy_list(chunk->_priv->infos); |
328 | 127k | wmem_list_foreach(chunk->errors, wscbor_subitem_free, alloc); |
329 | 127k | wmem_destroy_list(chunk->errors); |
330 | 127k | wmem_list_foreach(chunk->tags, wscbor_subitem_free, alloc); |
331 | 127k | wmem_destroy_list(chunk->tags); |
332 | 127k | wmem_free(alloc, chunk); |
333 | 127k | } |
334 | | |
335 | | /// User data for wscbor_expert_add() |
336 | | typedef struct { |
337 | | packet_info *pinfo; |
338 | | proto_item *item; |
339 | | } wscbor_expert_add_t; |
340 | | |
341 | | /// A callback for wmem_list_foreach() to add the info |
342 | 55.0k | static void wscbor_expert_add(void *data, void *userdata) { |
343 | 55.0k | const wscbor_error_t *err = (const wscbor_error_t *)data; |
344 | 55.0k | wscbor_expert_add_t *ctx = (wscbor_expert_add_t *)userdata; |
345 | | |
346 | 55.0k | if (err->msg) { |
347 | 48.5k | expert_add_info_format(ctx->pinfo, ctx->item, err->ei, "%s", err->msg); |
348 | 48.5k | } |
349 | 6.49k | else { |
350 | 6.49k | expert_add_info(ctx->pinfo, ctx->item, err->ei); |
351 | 6.49k | } |
352 | 55.0k | } |
353 | | |
354 | 83.5k | uint64_t wscbor_chunk_mark_errors(packet_info *pinfo, proto_item *item, const wscbor_chunk_t *chunk) { |
355 | 83.5k | wscbor_expert_add_t ctx = {pinfo, item}; |
356 | 83.5k | wmem_list_foreach(chunk->errors, wscbor_expert_add, &ctx); |
357 | 83.5k | wmem_list_foreach(chunk->_priv->infos, wscbor_expert_add, &ctx); |
358 | | |
359 | 83.5k | return wmem_list_count(chunk->errors); |
360 | 83.5k | } |
361 | | |
362 | 209k | unsigned wscbor_has_errors(const wscbor_chunk_t *chunk) { |
363 | 209k | return wmem_list_count(chunk->errors); |
364 | 209k | } |
365 | | |
366 | 110k | bool wscbor_is_indefinite_break(const wscbor_chunk_t *chunk) { |
367 | 110k | return ( |
368 | 110k | (chunk->type_major == CBOR_TYPE_FLOAT_CTRL) |
369 | 17.3k | && (chunk->type_minor == 31) |
370 | 110k | ); |
371 | 110k | } |
372 | | |
373 | | /** Add output parameter to indicate internal state. |
374 | | * @param alloc The allocator to use. |
375 | | * @param tvb The data buffer. |
376 | | * @param[in,out] offset The initial offset to read and skip over. |
377 | | * @param[out] is_break If non-null, set to true only when the item was |
378 | | * an indefinite break. |
379 | | * @return True if the skipped item was fully valid. |
380 | | */ |
381 | | // NOLINTNEXTLINE(misc-no-recursion) |
382 | 172k | static bool wscbor_skip_next_item_internal(wmem_allocator_t *alloc, tvbuff_t *tvb, int *offset, bool *is_break, unsigned depth) { |
383 | 172k | if (depth > prefs.gui_max_tree_depth) { |
384 | | |
385 | 121 | return false; |
386 | 121 | } |
387 | 172k | wscbor_chunk_t *chunk = wscbor_chunk_read(alloc, tvb, offset); |
388 | 172k | if (wscbor_has_errors(chunk)) { |
389 | 6.23k | wscbor_chunk_free(chunk); |
390 | 6.23k | return false; |
391 | 6.23k | } |
392 | 166k | switch (chunk->type_major) { |
393 | 53.3k | case CBOR_TYPE_UINT: |
394 | 67.6k | case CBOR_TYPE_NEGINT: |
395 | 67.6k | case CBOR_TYPE_TAG: |
396 | 83.6k | case CBOR_TYPE_FLOAT_CTRL: |
397 | 83.6k | break; |
398 | 2.55k | case CBOR_TYPE_BYTESTRING: |
399 | 7.07k | case CBOR_TYPE_STRING: |
400 | | // wscbor_chunk_read() sets offset past string value |
401 | 7.07k | break; |
402 | 47.9k | case CBOR_TYPE_ARRAY: { |
403 | 47.9k | if (chunk->type_minor == 31) { |
404 | | // wait for indefinite break |
405 | 1.26k | bool was_break = false; |
406 | 3.33k | do { |
407 | 3.33k | if (!wscbor_skip_next_item_internal(alloc, tvb, offset, &was_break, depth + 1)) { |
408 | 835 | return false; |
409 | 835 | } |
410 | 3.33k | } |
411 | 2.49k | while (!was_break); |
412 | 1.26k | } |
413 | 46.6k | else { |
414 | 46.6k | const uint64_t count = chunk->head_value; |
415 | 58.9k | for (uint64_t ix = 0; ix < count; ++ix) { |
416 | 55.3k | if (!wscbor_skip_next_item_internal(alloc, tvb, offset, NULL, depth + 1)) { |
417 | 43.1k | return false; |
418 | 43.1k | } |
419 | 55.3k | } |
420 | 46.6k | } |
421 | 4.01k | break; |
422 | 47.9k | } |
423 | 27.6k | case CBOR_TYPE_MAP: { |
424 | 27.6k | if (chunk->type_minor == 31) { |
425 | | // wait for indefinite break |
426 | 1.52k | bool was_break = false; |
427 | 4.68k | do { |
428 | 4.68k | if (!wscbor_skip_next_item_internal(alloc, tvb, offset, &was_break, depth + 1)) { |
429 | 1.04k | return false; |
430 | 1.04k | } |
431 | 4.68k | } |
432 | 3.64k | while (!was_break); |
433 | 1.52k | } |
434 | 26.1k | else { |
435 | 26.1k | const uint64_t count = chunk->head_value; |
436 | 31.3k | for (uint64_t ix = 0; ix < count; ++ix) { |
437 | 29.6k | if (!wscbor_skip_next_item_internal(alloc, tvb, offset, NULL, depth + 1)) { |
438 | 23.7k | return false; |
439 | 23.7k | } |
440 | 5.85k | if (!wscbor_skip_next_item_internal(alloc, tvb, offset, NULL, depth + 1)) { |
441 | 605 | return false; |
442 | 605 | } |
443 | 5.85k | } |
444 | 26.1k | } |
445 | 2.22k | break; |
446 | 27.6k | } |
447 | 166k | } |
448 | 96.5k | const bool got_break = wscbor_is_indefinite_break(chunk); |
449 | 96.5k | if (is_break) { |
450 | 6.09k | *is_break = got_break; |
451 | 6.09k | } |
452 | 96.5k | wscbor_chunk_free(chunk); |
453 | | // RFC 8949 Sec 3.2.1: a break code outside of an indefinite container is |
454 | | // not valid, and is_break is non-null only in indefinite container. |
455 | 96.5k | return is_break || !got_break; |
456 | 166k | } |
457 | | |
458 | 73.9k | bool wscbor_skip_next_item(wmem_allocator_t *alloc, tvbuff_t *tvb, int *offset) { |
459 | 73.9k | return wscbor_skip_next_item_internal(alloc, tvb, offset, NULL, 0); |
460 | 73.9k | } |
461 | | |
462 | 37.1k | bool wscbor_skip_if_errors(wmem_allocator_t *alloc, tvbuff_t *tvb, int *offset, const wscbor_chunk_t *chunk) { |
463 | 37.1k | if (wscbor_has_errors(chunk) == 0) { |
464 | 3.06k | return false; |
465 | 3.06k | } |
466 | | |
467 | 34.0k | *offset = chunk->start; |
468 | 34.0k | wscbor_skip_next_item(alloc, tvb, offset); |
469 | 34.0k | return true; |
470 | 37.1k | } |
471 | | |
472 | 15 | void wscbor_init(void) { |
473 | 15 | proto_wscbor = proto_register_protocol("CBOR Item Decoder", "CBOR Item Decoder", "_ws.wscbor"); |
474 | | |
475 | 15 | expert_module_t *expert_wscbor = expert_register_protocol(proto_wscbor); |
476 | | /* This isn't really a protocol, it's an error indication; |
477 | | disabling them makes no sense. */ |
478 | 15 | proto_set_cant_toggle(proto_wscbor); |
479 | | |
480 | 15 | expert_register_field_array(expert_wscbor, expertitems, array_length(expertitems)); |
481 | 15 | } |
482 | | |
483 | 0 | const ei_register_info * wscbor_expert_items(int *size) { |
484 | 0 | if (size) { |
485 | 0 | *size = array_length(expertitems); |
486 | 0 | } |
487 | 0 | return expertitems; |
488 | 0 | } |
489 | | |
490 | 51.1k | bool wscbor_require_major_type(wscbor_chunk_t *chunk, cbor_type major) { |
491 | 51.1k | if (chunk->type_major == major) { |
492 | 12.1k | return true; |
493 | 12.1k | } |
494 | 38.9k | wmem_list_append(chunk->errors, wscbor_error_new( |
495 | 38.9k | chunk->_priv->alloc, &ei_cbor_wrong_type, |
496 | 38.9k | "Item has major type %d, should be %d", |
497 | 38.9k | chunk->type_major, major |
498 | 38.9k | )); |
499 | 38.9k | return false; |
500 | 51.1k | } |
501 | | |
502 | 16.1k | bool wscbor_require_array(wscbor_chunk_t *chunk) { |
503 | 16.1k | return wscbor_require_major_type(chunk, CBOR_TYPE_ARRAY); |
504 | 16.1k | } |
505 | | |
506 | 15.2k | bool wscbor_require_array_size(wscbor_chunk_t *chunk, uint64_t count_min, uint64_t count_max) { |
507 | 15.2k | if (!wscbor_require_array(chunk)) { |
508 | 12.9k | return false; |
509 | 12.9k | } |
510 | 2.24k | if ((chunk->head_value < count_min) || (chunk->head_value > count_max)) { |
511 | 855 | wmem_list_append(chunk->errors, wscbor_error_new( |
512 | 855 | chunk->_priv->alloc, &ei_cbor_array_wrong_size, |
513 | 855 | "Array has %" PRId64 " items, should be within [%"PRId64", %"PRId64"]", |
514 | 855 | chunk->head_value, count_min, count_max |
515 | 855 | )); |
516 | 855 | return false; |
517 | 855 | } |
518 | 1.39k | return true; |
519 | 2.24k | } |
520 | | |
521 | 22.2k | bool wscbor_require_map(wscbor_chunk_t *chunk) { |
522 | 22.2k | return wscbor_require_major_type(chunk, CBOR_TYPE_MAP); |
523 | 22.2k | } |
524 | | |
525 | 70 | bool * wscbor_require_boolean(wmem_allocator_t *alloc, wscbor_chunk_t *chunk) { |
526 | 70 | if (!wscbor_require_major_type(chunk, CBOR_TYPE_FLOAT_CTRL)) { |
527 | 0 | return NULL; |
528 | 0 | } |
529 | | |
530 | 70 | switch (chunk->type_minor) { |
531 | 1 | case CBOR_CTRL_TRUE: |
532 | 1 | case CBOR_CTRL_FALSE: { |
533 | 1 | bool *value = NULL; |
534 | 1 | value = wmem_new(alloc, bool); |
535 | 1 | *value = (chunk->type_minor == CBOR_CTRL_TRUE); |
536 | 1 | return value; |
537 | 1 | } |
538 | 69 | default: |
539 | 69 | wmem_list_append(chunk->errors, wscbor_error_new( |
540 | 69 | chunk->_priv->alloc, &ei_cbor_wrong_type, |
541 | 69 | "Item has minor type %d, should be %d or %d", |
542 | 69 | chunk->type_minor, CBOR_CTRL_TRUE, CBOR_CTRL_FALSE |
543 | 69 | )); |
544 | 69 | break; |
545 | 70 | } |
546 | 69 | return NULL; |
547 | 70 | } |
548 | | |
549 | 6.04k | uint64_t * wscbor_require_uint64(wmem_allocator_t *alloc, wscbor_chunk_t *chunk) { |
550 | 6.04k | if (!wscbor_require_major_type(chunk, CBOR_TYPE_UINT)) { |
551 | 2.30k | return NULL; |
552 | 2.30k | } |
553 | | |
554 | 3.73k | uint64_t *result = wmem_new(alloc, uint64_t); |
555 | 3.73k | *result = chunk->head_value; |
556 | 3.73k | return result; |
557 | 6.04k | } |
558 | | |
559 | 42.5k | int64_t * wscbor_require_int64(wmem_allocator_t *alloc, wscbor_chunk_t *chunk) { |
560 | 42.5k | int64_t *result = NULL; |
561 | 42.5k | switch (chunk->type_major) { |
562 | 28.0k | case CBOR_TYPE_UINT: |
563 | 37.0k | case CBOR_TYPE_NEGINT: { |
564 | 37.0k | int64_t clamped; |
565 | 37.0k | if (chunk->head_value > INT64_MAX) { |
566 | 147 | clamped = INT64_MAX; |
567 | 147 | wmem_list_append(chunk->errors, wscbor_error_new( |
568 | 147 | chunk->_priv->alloc, &ei_cbor_overflow, |
569 | 147 | NULL |
570 | 147 | )); |
571 | 147 | } |
572 | 36.8k | else { |
573 | 36.8k | clamped = chunk->head_value; |
574 | 36.8k | } |
575 | | |
576 | 37.0k | result = wmem_new(alloc, int64_t); |
577 | 37.0k | if (chunk->type_major == CBOR_TYPE_NEGINT) { |
578 | 9.01k | *result = -clamped - 1; |
579 | 9.01k | } |
580 | 28.0k | else { |
581 | 28.0k | *result = clamped; |
582 | 28.0k | } |
583 | 37.0k | break; |
584 | 28.0k | } |
585 | 5.50k | default: |
586 | 5.50k | wmem_list_append(chunk->errors, wscbor_error_new( |
587 | 5.50k | chunk->_priv->alloc, &ei_cbor_wrong_type, |
588 | 5.50k | "Item has major type %d, should be %d or %d", |
589 | 5.50k | chunk->type_major, CBOR_TYPE_UINT, CBOR_TYPE_NEGINT |
590 | 5.50k | )); |
591 | 5.50k | break; |
592 | 42.5k | } |
593 | 42.5k | return result; |
594 | 42.5k | } |
595 | | |
596 | 3.06k | char * wscbor_require_tstr(wmem_allocator_t *alloc, wscbor_chunk_t *chunk) { |
597 | 3.06k | if (!wscbor_require_major_type(chunk, CBOR_TYPE_STRING)) { |
598 | 0 | return NULL; |
599 | 0 | } |
600 | | |
601 | 3.06k | return (char *)tvb_get_string_enc(alloc, chunk->_priv->str_value, 0, tvb_reported_length(chunk->_priv->str_value), ENC_UTF_8); |
602 | 3.06k | } |
603 | | |
604 | 3.47k | tvbuff_t * wscbor_require_bstr(wmem_allocator_t *alloc _U_, wscbor_chunk_t *chunk) { |
605 | 3.47k | if (!wscbor_require_major_type(chunk, CBOR_TYPE_BYTESTRING)) { |
606 | 2.59k | return NULL; |
607 | 2.59k | } |
608 | | |
609 | 877 | return chunk->_priv->str_value; |
610 | 3.47k | } |
611 | | |
612 | 747 | proto_item * proto_tree_add_cbor_container(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk) { |
613 | 747 | const header_field_info *hfinfo = proto_registrar_get_nth(hfindex); |
614 | 747 | proto_item *item; |
615 | 747 | if (FT_IS_UINT(hfinfo->type)) { |
616 | 157 | item = proto_tree_add_uint64(tree, hfindex, tvb, chunk->start, chunk->head_length, chunk->head_value); |
617 | 157 | } |
618 | 590 | else if (FT_IS_INT(hfinfo->type)) { |
619 | 0 | item = proto_tree_add_int64(tree, hfindex, tvb, chunk->start, chunk->head_length, chunk->head_value); |
620 | 0 | } |
621 | 590 | else { |
622 | 590 | item = proto_tree_add_item(tree, hfindex, tvb, chunk->start, -1, 0); |
623 | 590 | } |
624 | 747 | wscbor_chunk_mark_errors(pinfo, item, chunk); |
625 | 747 | return item; |
626 | 747 | } |
627 | | |
628 | 146 | proto_item * proto_tree_add_cbor_ctrl(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk) { |
629 | 146 | proto_item *item = proto_tree_add_item(tree, hfindex, tvb, chunk->start, chunk->head_length, 0); |
630 | 146 | wscbor_chunk_mark_errors(pinfo, item, chunk); |
631 | 146 | return item; |
632 | 146 | } |
633 | | |
634 | 70 | 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) { |
635 | 70 | proto_item *item = proto_tree_add_boolean(tree, hfindex, tvb, chunk->start, chunk->data_length, value ? *value : false); |
636 | 70 | wscbor_chunk_mark_errors(pinfo, item, chunk); |
637 | 70 | return item; |
638 | 70 | } |
639 | | |
640 | 4.78k | 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) { |
641 | 4.78k | proto_item *item = proto_tree_add_uint64(tree, hfindex, tvb, chunk->start, chunk->head_length, value ? *value : 0); |
642 | 4.78k | wscbor_chunk_mark_errors(pinfo, item, chunk); |
643 | 4.78k | return item; |
644 | 4.78k | } |
645 | | |
646 | 35.2k | 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) { |
647 | 35.2k | proto_item *item = proto_tree_add_int64(tree, hfindex, tvb, chunk->start, chunk->head_length, value ? *value : 0); |
648 | 35.2k | wscbor_chunk_mark_errors(pinfo, item, chunk); |
649 | 35.2k | return item; |
650 | 35.2k | } |
651 | | |
652 | | proto_item * proto_tree_add_cbor_bitmask(proto_tree *tree, int hfindex, const int ett, |
653 | | int *const *fields, packet_info *pinfo, |
654 | | tvbuff_t *tvb, const wscbor_chunk_t *chunk, |
655 | | const uint64_t *value) |
656 | 1.25k | { |
657 | 1.25k | const uint64_t v = value ? *value : 0; |
658 | | |
659 | 1.25k | proto_item *ti = proto_tree_add_uint64(tree, hfindex, tvb, chunk->start, chunk->head_length, v); |
660 | 1.25k | proto_tree *sub = proto_item_add_subtree(ti, ett); |
661 | | |
662 | 1.25k | if (fields) { |
663 | 7.63k | for (int i = 0; fields[i] != NULL; i++) { |
664 | 6.37k | proto_tree_add_boolean(sub, *fields[i], tvb, chunk->start, chunk->head_length, v); |
665 | 6.37k | } |
666 | 1.25k | } |
667 | | |
668 | 1.25k | wscbor_chunk_mark_errors(pinfo, ti, chunk); |
669 | 1.25k | return ti; |
670 | 1.25k | } |
671 | | |
672 | 1.19k | proto_item * proto_tree_add_cbor_tstr(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk) { |
673 | 1.19k | proto_item *item; |
674 | 1.19k | if (chunk->_priv->str_value) { |
675 | 1.19k | item = proto_tree_add_item(tree, hfindex, chunk->_priv->str_value, 0, tvb_reported_length(chunk->_priv->str_value), ENC_UTF_8); |
676 | 1.19k | } |
677 | 0 | else { |
678 | | // still show an empty item with errors |
679 | 0 | item = proto_tree_add_item(tree, hfindex, tvb, chunk->start, 0, 0); |
680 | 0 | } |
681 | 1.19k | wscbor_chunk_mark_errors(pinfo, item, chunk); |
682 | 1.19k | return item; |
683 | 1.19k | } |
684 | | |
685 | 3.44k | proto_item * proto_tree_add_cbor_bstr(proto_tree *tree, int hfindex, packet_info *pinfo, tvbuff_t *tvb, const wscbor_chunk_t *chunk) { |
686 | 3.44k | proto_item *item; |
687 | 3.44k | if (chunk->_priv->str_value) { |
688 | 934 | item = proto_tree_add_item(tree, hfindex, chunk->_priv->str_value, 0, tvb_reported_length(chunk->_priv->str_value), 0); |
689 | 934 | } |
690 | 2.51k | else { |
691 | | // still show an empty item with errors |
692 | 2.51k | item = proto_tree_add_item(tree, hfindex, tvb, chunk->start, 0, 0); |
693 | 2.51k | } |
694 | 3.44k | wscbor_chunk_mark_errors(pinfo, item, chunk); |
695 | 3.44k | return item; |
696 | 3.44k | } |
697 | | |
698 | 967 | proto_item * proto_tree_add_cbor_strlen(proto_tree *tree, int hfindex, packet_info *pinfo _U_, tvbuff_t *tvb, const wscbor_chunk_t *chunk) { |
699 | 967 | const unsigned str_len = (chunk->_priv->str_value ? tvb_reported_length(chunk->_priv->str_value) : 0); |
700 | 967 | proto_item *item = proto_tree_add_uint64(tree, hfindex, tvb, chunk->start, chunk->head_length, str_len); |
701 | 967 | return item; |
702 | 967 | } |