/* tvbuff_zstd.c * Copyright 2022, Kevin Albertson * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ /* * Decompress ZSTD: http://facebook.github.io/zstd/ */ #include "config.h" #ifdef HAVE_ZSTD #include #endif #include "proto.h" // DISSECTOR_ASSERT_HINT #include "tvbuff.h" #include "tvbuff-int.h" // tvb_add_to_chain #define MAX_LOOP_ITERATIONS 100 tvbuff_t *tvb_uncompress_zstd(tvbuff_t *tvb, const int offset, int comprlen) { #ifndef HAVE_ZSTD // Cast to void to silence unused warnings. (void)tvb; (void)offset; (void)comprlen; return NULL; #else ZSTD_inBuffer input = {tvb_memdup(NULL, tvb, offset, comprlen), comprlen, 0}; ZSTD_DStream *zds = ZSTD_createDStream(); size_t rc = 0; uint8_t *uncompr = NULL; size_t uncompr_len = 0; bool ok = false; int count = 0; // ZSTD does not consume the last byte of the frame until it has flushed all of the decompressed data of the frame. // Therefore, loop while there is more input. ZSTD_outBuffer output = {g_malloc(ZSTD_DStreamOutSize()), ZSTD_DStreamOutSize(), 0}; while (input.pos < input.size && count < MAX_LOOP_ITERATIONS) { rc = ZSTD_decompressStream(zds, &output, &input); if (ZSTD_isError(rc)) { goto end; } if (output.pos > 0) { if (!uncompr) { DISSECTOR_ASSERT (uncompr_len == 0); uncompr = g_malloc(output.pos); } else { uncompr = g_realloc(uncompr, uncompr_len + output.pos); } memcpy (uncompr + uncompr_len, output.dst, output.pos); uncompr_len += output.pos; // Reset the output buffer. output.pos = 0; } count++; DISSECTOR_ASSERT_HINT(count < MAX_LOOP_ITERATIONS, "MAX_LOOP_ITERATIONS exceeded"); } if (rc > 0) { // There is extra data that was not decompressed. goto end; } ok = true; end: g_free((void *)output.dst); wmem_free(NULL, (void *)input.src); ZSTD_freeDStream(zds); if (ok) { tvbuff_t *uncompr_tvb; uncompr_tvb = tvb_new_real_data (uncompr, (guint)uncompr_len, (guint)uncompr_len); tvb_set_free_cb (uncompr_tvb, g_free); return uncompr_tvb; } if (uncompr) { g_free (uncompr); } return NULL; #endif /* HAVE_ZSTD */ } tvbuff_t *tvb_child_uncompress_zstd(tvbuff_t *parent, tvbuff_t *tvb, const int offset, int comprlen) { tvbuff_t *uncompressed = tvb_uncompress_zstd(tvb, offset, comprlen); if (!uncompressed) { return uncompressed; } tvb_add_to_chain(parent, uncompressed); return uncompressed; }