diff options
author | Jakub Zawadzki <darkjames-ws@darkjames.pl> | 2013-12-06 23:23:44 +0000 |
---|---|---|
committer | Jakub Zawadzki <darkjames-ws@darkjames.pl> | 2013-12-06 23:23:44 +0000 |
commit | c1ef044de5e4a28cbad0d010aeafe38da0be6750 (patch) | |
tree | fce22addeb0c465278ffac2b56c7cd27955ffd7a /epan/tvbuff_zlib.c | |
parent | 895f83b37d70006a00f0b0e08667f9ffe9ea6dd4 (diff) | |
download | wireshark-c1ef044de5e4a28cbad0d010aeafe38da0be6750.tar.gz wireshark-c1ef044de5e4a28cbad0d010aeafe38da0be6750.tar.bz2 wireshark-c1ef044de5e4a28cbad0d010aeafe38da0be6750.zip |
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.c | 323 |
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; +} |