1 /* tvbuff_zlib.c
2 *
3 * Copyright (c) 2000 by Gilbert Ramirez <gram@alumni.rice.edu>
4 *
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12 #include <config.h>
13 #define WS_LOG_DOMAIN LOG_DOMAIN_EPAN
14
15 #include <glib.h>
16
17 #include <string.h>
18 #include <wsutil/glib-compat.h>
19
20 #ifdef HAVE_ZLIB
21 #define ZLIB_CONST
22 #include <zlib.h>
23 #endif
24
25 #include "tvbuff.h"
26 #include <wsutil/wslog.h>
27
28 #ifdef HAVE_ZLIB
29 /*
30 * Uncompresses a zlib compressed packet inside a message of tvb at offset with
31 * length comprlen. Returns an uncompressed tvbuffer if uncompression
32 * succeeded or NULL if uncompression failed.
33 */
34 #define TVB_Z_MIN_BUFSIZ 32768
35 #define TVB_Z_MAX_BUFSIZ 1048576 * 10
36
37 tvbuff_t *
tvb_uncompress(tvbuff_t * tvb,const int offset,int comprlen)38 tvb_uncompress(tvbuff_t *tvb, const int offset, int comprlen)
39 {
40 gint err;
41 guint bytes_out = 0;
42 guint8 *compr;
43 guint8 *uncompr = NULL;
44 tvbuff_t *uncompr_tvb = NULL;
45 z_streamp strm;
46 Bytef *strmbuf;
47 guint inits_done = 0;
48 gint wbits = MAX_WBITS;
49 guint8 *next;
50 guint bufsiz;
51 guint inflate_passes = 0;
52 guint bytes_in = tvb_captured_length_remaining(tvb, offset);
53
54 if (tvb == NULL || comprlen <= 0) {
55 return NULL;
56 }
57
58 compr = (guint8 *)tvb_memdup(NULL, tvb, offset, comprlen);
59 if (compr == NULL) {
60 return NULL;
61 }
62
63 /*
64 * Assume that the uncompressed data is at least twice as big as
65 * the compressed size.
66 */
67 bufsiz = tvb_captured_length_remaining(tvb, offset) * 2;
68 bufsiz = CLAMP(bufsiz, TVB_Z_MIN_BUFSIZ, TVB_Z_MAX_BUFSIZ);
69
70 ws_debug("bufsiz: %u bytes\n", bufsiz);
71
72 next = compr;
73
74 strm = g_new0(z_stream, 1);
75 strm->next_in = next;
76 strm->avail_in = comprlen;
77
78 strmbuf = (Bytef *)g_malloc0(bufsiz);
79 strm->next_out = strmbuf;
80 strm->avail_out = bufsiz;
81
82 err = inflateInit2(strm, wbits);
83 inits_done = 1;
84 if (err != Z_OK) {
85 inflateEnd(strm);
86 g_free(strm);
87 wmem_free(NULL, compr);
88 g_free(strmbuf);
89 return NULL;
90 }
91
92 while (1) {
93 memset(strmbuf, '\0', bufsiz);
94 strm->next_out = strmbuf;
95 strm->avail_out = bufsiz;
96
97 err = inflate(strm, Z_SYNC_FLUSH);
98
99 if (err == Z_OK || err == Z_STREAM_END) {
100 guint bytes_pass = bufsiz - strm->avail_out;
101
102 ++inflate_passes;
103
104 if (uncompr == NULL) {
105 /*
106 * This is ugly workaround for bug #6480
107 * (https://gitlab.com/wireshark/wireshark/-/issues/6480)
108 *
109 * g_memdup2(..., 0) returns NULL (g_malloc(0) also)
110 * when uncompr is NULL logic below doesn't create tvb
111 * which is later interpreted as decompression failed.
112 */
113 uncompr = (guint8 *)((bytes_pass || err != Z_STREAM_END) ?
114 g_memdup2(strmbuf, bytes_pass) :
115 g_strdup(""));
116 } else {
117 guint8 *new_data = (guint8 *)g_malloc0(bytes_out + bytes_pass);
118
119 memcpy(new_data, uncompr, bytes_out);
120 memcpy(new_data + bytes_out, strmbuf, bytes_pass);
121
122 g_free(uncompr);
123 uncompr = new_data;
124 }
125
126 bytes_out += bytes_pass;
127
128 if (err == Z_STREAM_END) {
129 inflateEnd(strm);
130 g_free(strm);
131 g_free(strmbuf);
132 break;
133 }
134 } else if (err == Z_BUF_ERROR) {
135 /*
136 * It's possible that not enough frames were captured
137 * to decompress this fully, so return what we've done
138 * so far, if any.
139 */
140 inflateEnd(strm);
141 g_free(strm);
142 g_free(strmbuf);
143
144 if (uncompr != NULL) {
145 break;
146 } else {
147 wmem_free(NULL, compr);
148 return NULL;
149 }
150
151 } else if (err == Z_DATA_ERROR && inits_done == 1
152 && uncompr == NULL && comprlen >= 2 &&
153 (*compr == 0x1f) && (*(compr + 1) == 0x8b)) {
154 /*
155 * inflate() is supposed to handle both gzip and deflate
156 * streams automatically, but in reality it doesn't
157 * seem to handle either (at least not within the
158 * context of an HTTP response.) We have to try
159 * several tweaks, depending on the type of data and
160 * version of the library installed.
161 */
162
163 /*
164 * Gzip file format. Skip past the header, since the
165 * fix to make it work (setting windowBits to 31)
166 * doesn't work with all versions of the library.
167 */
168 Bytef *c = compr + 2;
169 Bytef flags = 0;
170
171 /* we read two bytes already (0x1f, 0x8b) and
172 need at least Z_DEFLATED, 1 byte flags, 4
173 bytes MTIME, 1 byte XFL, 1 byte OS */
174 if (comprlen < 10 || *c != Z_DEFLATED) {
175 inflateEnd(strm);
176 g_free(strm);
177 wmem_free(NULL, compr);
178 g_free(strmbuf);
179 return NULL;
180 }
181
182 c++;
183 flags = *c;
184 c++;
185
186 /* Skip past the MTIME (4 bytes),
187 XFL, and OS fields (1 byte each). */
188 c += 6;
189
190 if (flags & (1 << 2)) {
191 /* An Extra field is present. It
192 consists of 2 bytes xsize and xsize
193 bytes of data.
194 Read byte-by-byte (least significant
195 byte first) to make sure we abort
196 cleanly when the xsize is truncated
197 after the first byte. */
198 guint16 xsize = 0;
199
200 if (c-compr < comprlen) {
201 xsize += *c;
202 c++;
203 }
204 if (c-compr < comprlen) {
205 xsize += *c << 8;
206 c++;
207 }
208
209 c += xsize;
210 }
211
212 if (flags & (1 << 3)) {
213 /* A null terminated filename */
214
215 while ((c - compr) < comprlen && *c != '\0') {
216 c++;
217 }
218
219 c++;
220 }
221
222 if (flags & (1 << 4)) {
223 /* A null terminated comment */
224
225 while ((c - compr) < comprlen && *c != '\0') {
226 c++;
227 }
228
229 c++;
230 }
231
232
233 if (c - compr > comprlen) {
234 inflateEnd(strm);
235 g_free(strm);
236 wmem_free(NULL, compr);
237 g_free(strmbuf);
238 return NULL;
239 }
240 /* Drop gzip header */
241 comprlen -= (int) (c - compr);
242 next = c;
243
244 inflateReset(strm);
245 strm->next_in = next;
246 strm->avail_in = comprlen;
247
248 inflateEnd(strm);
249 inflateInit2(strm, wbits);
250 inits_done++;
251 } else if (err == Z_DATA_ERROR && uncompr == NULL &&
252 inits_done <= 3) {
253
254 /*
255 * Re-init the stream with a negative
256 * MAX_WBITS. This is necessary due to
257 * some servers (Apache) not sending
258 * the deflate header with the
259 * content-encoded response.
260 */
261 wbits = -MAX_WBITS;
262
263 inflateReset(strm);
264
265 strm->next_in = next;
266 strm->avail_in = comprlen;
267
268 inflateEnd(strm);
269 memset(strmbuf, '\0', bufsiz);
270 strm->next_out = strmbuf;
271 strm->avail_out = bufsiz;
272
273 err = inflateInit2(strm, wbits);
274
275 inits_done++;
276
277 if (err != Z_OK) {
278 g_free(strm);
279 g_free(strmbuf);
280 wmem_free(NULL, compr);
281 g_free(uncompr);
282
283 return NULL;
284 }
285 } else {
286 inflateEnd(strm);
287 g_free(strm);
288 g_free(strmbuf);
289
290 if (uncompr == NULL) {
291 wmem_free(NULL, compr);
292 return NULL;
293 }
294
295 break;
296 }
297 }
298
299 ws_debug("inflate() total passes: %u\n", inflate_passes);
300 ws_debug("bytes in: %u\nbytes out: %u\n\n", bytes_in, bytes_out);
301
302 if (uncompr != NULL) {
303 uncompr_tvb = tvb_new_real_data(uncompr, bytes_out, bytes_out);
304 tvb_set_free_cb(uncompr_tvb, g_free);
305 }
306 wmem_free(NULL, compr);
307 return uncompr_tvb;
308 }
309 #else
310 tvbuff_t *
tvb_uncompress(tvbuff_t * tvb _U_,const int offset _U_,int comprlen _U_)311 tvb_uncompress(tvbuff_t *tvb _U_, const int offset _U_, int comprlen _U_)
312 {
313 return NULL;
314 }
315 #endif
316
317 tvbuff_t *
tvb_child_uncompress(tvbuff_t * parent,tvbuff_t * tvb,const int offset,int comprlen)318 tvb_child_uncompress(tvbuff_t *parent, tvbuff_t *tvb, const int offset, int comprlen)
319 {
320 tvbuff_t *new_tvb = tvb_uncompress(tvb, offset, comprlen);
321 if (new_tvb)
322 tvb_set_child_real_data_tvbuff (parent, new_tvb);
323 return new_tvb;
324 }
325
326 /*
327 * Editor modelines - https://www.wireshark.org/tools/modelines.html
328 *
329 * Local variables:
330 * c-basic-offset: 8
331 * tab-width: 8
332 * indent-tabs-mode: t
333 * End:
334 *
335 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
336 * :indentSize=8:tabSize=8:noTabs=false:
337 */
338