/src/wireshark/epan/tvbuff_zstd.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* tvbuff_zstd.c |
2 | | * Copyright 2022, Kevin Albertson <kevin.eric.albertson [AT] gmail.com> |
3 | | * |
4 | | * Wireshark - Network traffic analyzer |
5 | | * By Gerald Combs <gerald@wireshark.org> |
6 | | * Copyright 1998 Gerald Combs |
7 | | * |
8 | | * SPDX-License-Identifier: GPL-2.0-or-later |
9 | | */ |
10 | | |
11 | | /* |
12 | | * Decompress ZSTD: http://facebook.github.io/zstd/ |
13 | | */ |
14 | | |
15 | | #include "config.h" |
16 | | |
17 | | #ifdef HAVE_ZSTD |
18 | | #include <zstd.h> |
19 | | #endif |
20 | | |
21 | | #include "proto.h" // DISSECTOR_ASSERT_HINT |
22 | | #include "tvbuff.h" |
23 | | |
24 | | #include "tvbuff-int.h" // tvb_add_to_chain |
25 | | |
26 | | #define MAX_LOOP_ITERATIONS 100 |
27 | | |
28 | | tvbuff_t *tvb_uncompress_zstd(tvbuff_t *tvb, const int offset, int comprlen) |
29 | 0 | { |
30 | 0 | #ifndef HAVE_ZSTD |
31 | | // Cast to void to silence unused warnings. |
32 | 0 | (void)tvb; |
33 | 0 | (void)offset; |
34 | 0 | (void)comprlen; |
35 | 0 | return NULL; |
36 | | #else |
37 | | ZSTD_inBuffer input = {tvb_memdup(NULL, tvb, offset, comprlen), comprlen, 0}; |
38 | | ZSTD_DStream *zds = ZSTD_createDStream(); |
39 | | size_t rc = 0; |
40 | | uint8_t *uncompr = NULL; |
41 | | size_t uncompr_len = 0; |
42 | | bool ok = false; |
43 | | int count = 0; |
44 | | |
45 | | // ZSTD does not consume the last byte of the frame until it has flushed all of the decompressed data of the frame. |
46 | | // Therefore, loop while there is more input. |
47 | | ZSTD_outBuffer output = {g_malloc(ZSTD_DStreamOutSize()), ZSTD_DStreamOutSize(), 0}; |
48 | | while (input.pos < input.size && count < MAX_LOOP_ITERATIONS) |
49 | | { |
50 | | rc = ZSTD_decompressStream(zds, &output, &input); |
51 | | if (ZSTD_isError(rc)) |
52 | | { |
53 | | goto end; |
54 | | } |
55 | | |
56 | | if (output.pos > 0) |
57 | | { |
58 | | if (!uncompr) |
59 | | { |
60 | | DISSECTOR_ASSERT (uncompr_len == 0); |
61 | | uncompr = g_malloc(output.pos); |
62 | | } else { |
63 | | uncompr = g_realloc(uncompr, uncompr_len + output.pos); |
64 | | } |
65 | | memcpy (uncompr + uncompr_len, output.dst, output.pos); |
66 | | uncompr_len += output.pos; |
67 | | // Reset the output buffer. |
68 | | output.pos = 0; |
69 | | } |
70 | | count++; |
71 | | DISSECTOR_ASSERT_HINT(count < MAX_LOOP_ITERATIONS, "MAX_LOOP_ITERATIONS exceeded"); |
72 | | } |
73 | | if (rc > 0) |
74 | | { |
75 | | // There is extra data that was not decompressed. |
76 | | goto end; |
77 | | } |
78 | | |
79 | | ok = true; |
80 | | end: |
81 | | g_free((void *)output.dst); |
82 | | wmem_free(NULL, (void *)input.src); |
83 | | ZSTD_freeDStream(zds); |
84 | | if (ok) |
85 | | { |
86 | | tvbuff_t *uncompr_tvb; |
87 | | uncompr_tvb = tvb_new_real_data (uncompr, (unsigned)uncompr_len, (unsigned)uncompr_len); |
88 | | tvb_set_free_cb (uncompr_tvb, g_free); |
89 | | return uncompr_tvb; |
90 | | } |
91 | | |
92 | | if (uncompr) |
93 | | { |
94 | | g_free (uncompr); |
95 | | } |
96 | | |
97 | | return NULL; |
98 | | #endif /* HAVE_ZSTD */ |
99 | 0 | } |
100 | | |
101 | | tvbuff_t *tvb_child_uncompress_zstd(tvbuff_t *parent, tvbuff_t *tvb, const int offset, int comprlen) |
102 | 0 | { |
103 | 0 | tvbuff_t *uncompressed = tvb_uncompress_zstd(tvb, offset, comprlen); |
104 | 0 | if (!uncompressed) |
105 | 0 | { |
106 | 0 | return uncompressed; |
107 | 0 | } |
108 | 0 | tvb_add_to_chain(parent, uncompressed); |
109 | 0 | return uncompressed; |
110 | 0 | } |