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