wireshark/epan/tvbuff_zstd.c

111 lines
2.8 KiB
C

/* tvbuff_zstd.c
* Copyright 2022, Kevin Albertson <kevin.eric.albertson [AT] gmail.com>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* 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 <zstd.h>
#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;
}