summaryrefslogtreecommitdiff
path: root/epan/tvbuff_zlib.c
diff options
context:
space:
mode:
authorJakub Zawadzki <darkjames-ws@darkjames.pl>2013-12-06 23:23:44 +0000
committerJakub Zawadzki <darkjames-ws@darkjames.pl>2013-12-06 23:23:44 +0000
commitc1ef044de5e4a28cbad0d010aeafe38da0be6750 (patch)
treefce22addeb0c465278ffac2b56c7cd27955ffd7a /epan/tvbuff_zlib.c
parent895f83b37d70006a00f0b0e08667f9ffe9ea6dd4 (diff)
downloadwireshark-c1ef044de5e4a28cbad0d010aeafe38da0be6750.tar.gz
Move tvb_uncompress() to tvbuff_zlib.c
svn path=/trunk/; revision=53815
Diffstat (limited to 'epan/tvbuff_zlib.c')
-rw-r--r--epan/tvbuff_zlib.c323
1 files changed, 323 insertions, 0 deletions
diff --git a/epan/tvbuff_zlib.c b/epan/tvbuff_zlib.c
new file mode 100644
index 0000000000..4f8e2e9dbe
--- /dev/null
+++ b/epan/tvbuff_zlib.c
@@ -0,0 +1,323 @@
+/* tvbuff_zlib.c
+ *
+ * $Id$
+ *
+ * Copyright (c) 2000 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include <string.h>
+
+#ifdef HAVE_LIBZ
+#include <zlib.h>
+#endif
+
+#include "tvbuff.h"
+
+#ifdef HAVE_LIBZ
+/*
+ * Uncompresses a zlib compressed packet inside a message of tvb at offset with
+ * length comprlen. Returns an uncompressed tvbuffer if uncompression
+ * succeeded or NULL if uncompression failed.
+ */
+#define TVB_Z_MIN_BUFSIZ 32768
+#define TVB_Z_MAX_BUFSIZ 1048576 * 10
+/* #define TVB_Z_DEBUG 1 */
+#undef TVB_Z_DEBUG
+
+tvbuff_t *
+tvb_uncompress(tvbuff_t *tvb, const int offset, int comprlen)
+{
+ gint err = Z_OK;
+ guint bytes_out = 0;
+ guint8 *compr = NULL;
+ guint8 *uncompr = NULL;
+ tvbuff_t *uncompr_tvb = NULL;
+ z_streamp strm = NULL;
+ Bytef *strmbuf = NULL;
+ guint inits_done = 0;
+ gint wbits = MAX_WBITS;
+ guint8 *next = NULL;
+ guint bufsiz = TVB_Z_MIN_BUFSIZ;
+#ifdef TVB_Z_DEBUG
+ guint inflate_passes = 0;
+ guint bytes_in = tvb_length_remaining(tvb, offset);
+#endif
+
+ if (tvb == NULL) {
+ return NULL;
+ }
+
+ compr = (guint8 *)tvb_memdup(NULL, tvb, offset, comprlen);
+
+ if (!compr)
+ return NULL;
+
+ /*
+ * Assume that the uncompressed data is at least twice as big as
+ * the compressed size.
+ */
+ bufsiz = tvb_length_remaining(tvb, offset) * 2;
+ bufsiz = CLAMP(bufsiz, TVB_Z_MIN_BUFSIZ, TVB_Z_MAX_BUFSIZ);
+
+#ifdef TVB_Z_DEBUG
+ printf("bufsiz: %u bytes\n", bufsiz);
+#endif
+
+ next = compr;
+
+ strm = g_new0(z_stream, 1);
+ strm->next_in = next;
+ strm->avail_in = comprlen;
+
+ strmbuf = (Bytef *)g_malloc0(bufsiz);
+ strm->next_out = strmbuf;
+ strm->avail_out = bufsiz;
+
+ err = inflateInit2(strm, wbits);
+ inits_done = 1;
+ if (err != Z_OK) {
+ inflateEnd(strm);
+ g_free(strm);
+ g_free(compr);
+ g_free(strmbuf);
+ return NULL;
+ }
+
+ while (1) {
+ memset(strmbuf, '\0', bufsiz);
+ strm->next_out = strmbuf;
+ strm->avail_out = bufsiz;
+
+ err = inflate(strm, Z_SYNC_FLUSH);
+
+ if (err == Z_OK || err == Z_STREAM_END) {
+ guint bytes_pass = bufsiz - strm->avail_out;
+
+#ifdef TVB_Z_DEBUG
+ ++inflate_passes;
+#endif
+
+ if (uncompr == NULL) {
+ /*
+ * This is ugly workaround for bug #6480
+ * (https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=6480)
+ *
+ * g_memdup(..., 0) returns NULL (g_malloc(0) also)
+ * when uncompr is NULL logic below doesn't create tvb
+ * which is later interpreted as decompression failed.
+ */
+ uncompr = (guint8 *)((bytes_pass || err != Z_STREAM_END) ?
+ g_memdup(strmbuf, bytes_pass) :
+ g_strdup(""));
+ } else {
+ guint8 *new_data = (guint8 *)g_malloc0(bytes_out + bytes_pass);
+
+ memcpy(new_data, uncompr, bytes_out);
+ memcpy(new_data + bytes_out, strmbuf, bytes_pass);
+
+ g_free(uncompr);
+ uncompr = new_data;
+ }
+
+ bytes_out += bytes_pass;
+
+ if (err == Z_STREAM_END) {
+ inflateEnd(strm);
+ g_free(strm);
+ g_free(strmbuf);
+ break;
+ }
+ } else if (err == Z_BUF_ERROR) {
+ /*
+ * It's possible that not enough frames were captured
+ * to decompress this fully, so return what we've done
+ * so far, if any.
+ */
+ inflateEnd(strm);
+ g_free(strm);
+ g_free(strmbuf);
+
+ if (uncompr != NULL) {
+ break;
+ } else {
+ g_free(compr);
+ return NULL;
+ }
+
+ } else if (err == Z_DATA_ERROR && inits_done == 1
+ && uncompr == NULL && (*compr == 0x1f) &&
+ (*(compr + 1) == 0x8b)) {
+ /*
+ * inflate() is supposed to handle both gzip and deflate
+ * streams automatically, but in reality it doesn't
+ * seem to handle either (at least not within the
+ * context of an HTTP response.) We have to try
+ * several tweaks, depending on the type of data and
+ * version of the library installed.
+ */
+
+ /*
+ * Gzip file format. Skip past the header, since the
+ * fix to make it work (setting windowBits to 31)
+ * doesn't work with all versions of the library.
+ */
+ Bytef *c = compr + 2;
+ Bytef flags = 0;
+
+ if (*c == Z_DEFLATED) {
+ c++;
+ } else {
+ inflateEnd(strm);
+ g_free(strm);
+ g_free(compr);
+ g_free(strmbuf);
+ return NULL;
+ }
+
+ flags = *c;
+
+ /* Skip past the MTIME, XFL, and OS fields. */
+ c += 7;
+
+ if (flags & (1 << 2)) {
+ /* An Extra field is present. */
+ gint xsize = (gint)(*c |
+ (*(c + 1) << 8));
+
+ c += xsize;
+ }
+
+ if (flags & (1 << 3)) {
+ /* A null terminated filename */
+
+ while ((c - compr) < comprlen && *c != '\0') {
+ c++;
+ }
+
+ c++;
+ }
+
+ if (flags & (1 << 4)) {
+ /* A null terminated comment */
+
+ while ((c - compr) < comprlen && *c != '\0') {
+ c++;
+ }
+
+ c++;
+ }
+
+
+ inflateReset(strm);
+ next = c;
+ strm->next_in = next;
+ if (c - compr > comprlen) {
+ inflateEnd(strm);
+ g_free(strm);
+ g_free(compr);
+ g_free(strmbuf);
+ return NULL;
+ }
+ comprlen -= (int) (c - compr);
+
+ inflateEnd(strm);
+ inflateInit2(strm, wbits);
+ inits_done++;
+ } else if (err == Z_DATA_ERROR && uncompr == NULL &&
+ inits_done <= 3) {
+
+ /*
+ * Re-init the stream with a negative
+ * MAX_WBITS. This is necessary due to
+ * some servers (Apache) not sending
+ * the deflate header with the
+ * content-encoded response.
+ */
+ wbits = -MAX_WBITS;
+
+ inflateReset(strm);
+
+ strm->next_in = next;
+ strm->avail_in = comprlen;
+
+ inflateEnd(strm);
+ memset(strmbuf, '\0', bufsiz);
+ strm->next_out = strmbuf;
+ strm->avail_out = bufsiz;
+
+ err = inflateInit2(strm, wbits);
+
+ inits_done++;
+
+ if (err != Z_OK) {
+ g_free(strm);
+ g_free(strmbuf);
+ g_free(compr);
+ g_free(uncompr);
+
+ return NULL;
+ }
+ } else {
+ inflateEnd(strm);
+ g_free(strm);
+ g_free(strmbuf);
+
+ if (uncompr == NULL) {
+ g_free(compr);
+ return NULL;
+ }
+
+ break;
+ }
+ }
+
+#ifdef TVB_Z_DEBUG
+ printf("inflate() total passes: %u\n", inflate_passes);
+ printf("bytes in: %u\nbytes out: %u\n\n", bytes_in, bytes_out);
+#endif
+
+ if (uncompr != NULL) {
+ uncompr_tvb = tvb_new_real_data((guint8*) uncompr, bytes_out, bytes_out);
+ tvb_set_free_cb(uncompr_tvb, g_free);
+ }
+ g_free(compr);
+ return uncompr_tvb;
+}
+#else
+tvbuff_t *
+tvb_uncompress(tvbuff_t *tvb _U_, const int offset _U_, int comprlen _U_)
+{
+ return NULL;
+}
+#endif
+
+tvbuff_t *
+tvb_child_uncompress(tvbuff_t *parent, tvbuff_t *tvb, const int offset, int comprlen)
+{
+ tvbuff_t *new_tvb = tvb_uncompress(tvb, offset, comprlen);
+ if (new_tvb)
+ tvb_set_child_real_data_tvbuff (parent, new_tvb);
+ return new_tvb;
+}