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