1 /* Copyright (c) 2004, Roger Dingledine.
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2011, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
8 * \brief A simple in-memory gzip implementation.
17 #include "..\..\contrib\zlib\zlib.h"
22 #ifdef HAVE_NETINET_IN_H
23 #include <netinet/in.h>
30 /** Set to 1 if zlib is a version that supports gzip; set to 0 if it doesn't;
31 * set to -1 if we haven't checked yet. */
32 static int gzip_is_supported
= -1;
34 /** Return true iff we support gzip-based compression. Otherwise, we need to
37 is_gzip_supported(void)
39 if (gzip_is_supported
>= 0)
40 return gzip_is_supported
;
42 if (!strcmpstart(ZLIB_VERSION
, "0.") ||
43 !strcmpstart(ZLIB_VERSION
, "1.0") ||
44 !strcmpstart(ZLIB_VERSION
, "1.1"))
45 gzip_is_supported
= 0;
47 gzip_is_supported
= 1;
49 return gzip_is_supported
;
52 /** Return the 'bits' value to tell zlib to use <b>method</b>.*/
54 method_bits(compress_method_t method
)
56 /* Bits+16 means "use gzip" in zlib >= 1.2 */
57 return method
== GZIP_METHOD
? 15+16 : 15;
60 /** Given <b>in_len</b> bytes at <b>in</b>, compress them into a newly
61 * allocated buffer, using the method described in <b>method</b>. Store the
62 * compressed string in *<b>out</b>, and its length in *<b>out_len</b>.
63 * Return 0 on success, -1 on failure.
66 tor_gzip_compress(char **out
, size_t *out_len
,
67 const char *in
, size_t in_len
,
68 compress_method_t method
)
70 struct z_stream_s
*stream
= NULL
;
71 size_t out_size
, old_size
;
77 tor_assert(in_len
< UINT_MAX
);
81 if (method
== GZIP_METHOD
&& !is_gzip_supported()) {
82 /* Old zlib version don't support gzip in deflateInit2 */
83 log_warn(LD_BUG
, "Gzip not supported with zlib %s", ZLIB_VERSION
);
87 stream
= tor_malloc_zero(sizeof(struct z_stream_s
));
88 stream
->zalloc
= Z_NULL
;
89 stream
->zfree
= Z_NULL
;
90 stream
->opaque
= NULL
;
91 stream
->next_in
= (unsigned char*) in
;
92 stream
->avail_in
= (unsigned int)in_len
;
94 if (deflateInit2(stream
, Z_BEST_COMPRESSION
, Z_DEFLATED
,
96 8, Z_DEFAULT_STRATEGY
) != Z_OK
) {
97 log_warn(LD_GENERAL
, "Error from deflateInit2: %s",
98 stream
->msg
?stream
->msg
:"<no message>");
102 /* Guess 50% compression. */
103 out_size
= in_len
/ 2;
104 if (out_size
< 1024) out_size
= 1024;
105 *out
= tor_malloc(out_size
);
106 stream
->next_out
= (unsigned char*)*out
;
107 stream
->avail_out
= (unsigned int)out_size
;
110 switch (deflate(stream
, Z_FINISH
))
115 /* In case zlib doesn't work as I think .... */
116 if (stream
->avail_out
>= stream
->avail_in
+16)
119 offset
= stream
->next_out
- ((unsigned char*)*out
);
122 if (out_size
< old_size
) {
123 log_warn(LD_GENERAL
, "Size overflow in compression.");
126 *out
= tor_realloc(*out
, out_size
);
127 stream
->next_out
= (unsigned char*)(*out
+ offset
);
128 if (out_size
- offset
> UINT_MAX
) {
129 log_warn(LD_BUG
, "Ran over unsigned int limit of zlib while "
133 stream
->avail_out
= (unsigned int)(out_size
- offset
);
136 log_warn(LD_GENERAL
, "Gzip compression didn't finish: %s",
137 stream
->msg
? stream
->msg
: "<no message>");
142 *out_len
= stream
->total_out
;
144 /* "Hey Rocky! Watch me change an unsigned field to a signed field in a
146 * "Oh, that trick will just make people do unsafe casts to the unsigned
147 * type in their cross-platform code!"
148 * "Don't be foolish. I'm _sure_ they'll have the good sense to make sure
149 * the newly unsigned field isn't negative." */
150 tor_assert(stream
->total_out
>= 0);
152 if (((size_t)stream
->total_out
) > out_size
+ 4097) {
153 /* If we're wasting more than 4k, don't. */
154 *out
= tor_realloc(*out
, stream
->total_out
+ 1);
156 if (deflateEnd(stream
)!=Z_OK
) {
157 log_warn(LD_BUG
, "Error freeing gzip structures");
174 /** Given zero or more zlib-compressed or gzip-compressed strings of
176 * <b>in_len</b> bytes at <b>in</b>, uncompress them into a newly allocated
177 * buffer, using the method described in <b>method</b>. Store the uncompressed
178 * string in *<b>out</b>, and its length in *<b>out_len</b>. Return 0 on
179 * success, -1 on failure.
181 * If <b>complete_only</b> is true, we consider a truncated input as a
182 * failure; otherwise we decompress as much as we can. Warn about truncated
183 * or corrupt inputs at <b>protocol_warn_level</b>.
186 tor_gzip_uncompress(char **out
, size_t *out_len
,
187 const char *in
, size_t in_len
,
188 compress_method_t method
,
190 int protocol_warn_level
)
192 struct z_stream_s
*stream
= NULL
;
193 size_t out_size
, old_size
;
200 tor_assert(in_len
< UINT_MAX
);
202 if (method
== GZIP_METHOD
&& !is_gzip_supported()) {
203 /* Old zlib version don't support gzip in inflateInit2 */
204 log_warn(LD_BUG
, "Gzip not supported with zlib %s", ZLIB_VERSION
);
210 stream
= tor_malloc_zero(sizeof(struct z_stream_s
));
211 stream
->zalloc
= Z_NULL
;
212 stream
->zfree
= Z_NULL
;
213 stream
->opaque
= NULL
;
214 stream
->next_in
= (unsigned char*) in
;
215 stream
->avail_in
= (unsigned int)in_len
;
217 if (inflateInit2(stream
,
218 method_bits(method
)) != Z_OK
) {
219 log_warn(LD_GENERAL
, "Error from inflateInit2: %s",
220 stream
->msg
?stream
->msg
:"<no message>");
224 out_size
= in_len
* 2; /* guess 50% compression. */
225 if (out_size
< 1024) out_size
= 1024;
226 if (out_size
> UINT_MAX
)
229 *out
= tor_malloc(out_size
);
230 stream
->next_out
= (unsigned char*)*out
;
231 stream
->avail_out
= (unsigned int)out_size
;
234 switch (inflate(stream
, complete_only
? Z_FINISH
: Z_SYNC_FLUSH
))
237 if (stream
->avail_in
== 0)
239 /* There may be more compressed data here. */
240 if ((r
= inflateEnd(stream
)) != Z_OK
) {
241 log_warn(LD_BUG
, "Error freeing gzip structures");
244 if (inflateInit2(stream
, method_bits(method
)) != Z_OK
) {
245 log_warn(LD_GENERAL
, "Error from second inflateInit2: %s",
246 stream
->msg
?stream
->msg
:"<no message>");
251 if (!complete_only
&& stream
->avail_in
== 0)
253 /* In case zlib doesn't work as I think.... */
254 if (stream
->avail_out
>= stream
->avail_in
+16)
257 if (stream
->avail_out
> 0) {
258 log_fn(protocol_warn_level
, LD_PROTOCOL
,
259 "possible truncated or corrupt zlib data");
262 offset
= stream
->next_out
- (unsigned char*)*out
;
265 if (out_size
< old_size
) {
266 log_warn(LD_GENERAL
, "Size overflow in compression.");
269 *out
= tor_realloc(*out
, out_size
);
270 stream
->next_out
= (unsigned char*)(*out
+ offset
);
271 if (out_size
- offset
> UINT_MAX
) {
272 log_warn(LD_BUG
, "Ran over unsigned int limit of zlib while "
276 stream
->avail_out
= (unsigned int)(out_size
- offset
);
279 log_warn(LD_GENERAL
, "Gzip decompression returned an error: %s",
280 stream
->msg
? stream
->msg
: "<no message>");
285 *out_len
= stream
->next_out
- (unsigned char*)*out
;
286 r
= inflateEnd(stream
);
289 log_warn(LD_BUG
, "Error freeing gzip structures");
293 /* NUL-terminate output. */
294 if (out_size
== *out_len
)
295 *out
= tor_realloc(*out
, out_size
+ 1);
296 (*out
)[*out_len
] = '\0';
310 /** Try to tell whether the <b>in_len</b>-byte string in <b>in</b> is likely
311 * to be compressed or not. If it is, return the likeliest compression method.
312 * Otherwise, return UNKNOWN_METHOD.
315 detect_compression_method(const char *in
, size_t in_len
)
317 if (in_len
> 2 && !memcmp(in
, "\x1f\x8b", 2)) {
319 } else if (in_len
> 2 && (in
[0] & 0x0f) == 8 &&
320 (ntohs(get_uint16(in
)) % 31) == 0) {
323 return UNKNOWN_METHOD
;
327 /** Internal state for an incremental zlib compression/decompression. The
328 * body of this struct is not exposed. */
329 struct tor_zlib_state_t
{
330 struct z_stream_s stream
;
334 /** Construct and return a tor_zlib_state_t object using <b>method</b>. If
335 * <b>compress</b>, it's for compression; otherwise it's for
338 tor_zlib_new(int compress
, compress_method_t method
)
340 tor_zlib_state_t
*out
;
342 if (method
== GZIP_METHOD
&& !is_gzip_supported()) {
343 /* Old zlib version don't support gzip in inflateInit2 */
344 log_warn(LD_BUG
, "Gzip not supported with zlib %s", ZLIB_VERSION
);
348 out
= tor_malloc_zero(sizeof(tor_zlib_state_t
));
349 out
->stream
.zalloc
= Z_NULL
;
350 out
->stream
.zfree
= Z_NULL
;
351 out
->stream
.opaque
= NULL
;
352 out
->compress
= compress
;
354 if (deflateInit2(&out
->stream
, Z_BEST_COMPRESSION
, Z_DEFLATED
,
355 method_bits(method
), 8, Z_DEFAULT_STRATEGY
) != Z_OK
)
358 if (inflateInit2(&out
->stream
, method_bits(method
)) != Z_OK
)
368 /** Compress/decompress some bytes using <b>state</b>. Read up to
369 * *<b>in_len</b> bytes from *<b>in</b>, and write up to *<b>out_len</b> bytes
370 * to *<b>out</b>, adjusting the values as we go. If <b>finish</b> is true,
371 * we've reached the end of the input.
373 * Return TOR_ZLIB_DONE if we've finished the entire compression/decompression.
374 * Return TOR_ZLIB_OK if we're processed everything from the input.
375 * Return TOR_ZLIB_BUF_FULL if we're out of space on <b>out</b>.
376 * Return TOR_ZLIB_ERR if the stream is corrupt.
379 tor_zlib_process(tor_zlib_state_t
*state
,
380 char **out
, size_t *out_len
,
381 const char **in
, size_t *in_len
,
385 tor_assert(*in_len
<= UINT_MAX
);
386 tor_assert(*out_len
<= UINT_MAX
);
387 state
->stream
.next_in
= (unsigned char*) *in
;
388 state
->stream
.avail_in
= (unsigned int)*in_len
;
389 state
->stream
.next_out
= (unsigned char*) *out
;
390 state
->stream
.avail_out
= (unsigned int)*out_len
;
392 if (state
->compress
) {
393 err
= deflate(&state
->stream
, finish
? Z_FINISH
: Z_SYNC_FLUSH
);
395 err
= inflate(&state
->stream
, finish
? Z_FINISH
: Z_SYNC_FLUSH
);
398 *out
= (char*) state
->stream
.next_out
;
399 *out_len
= state
->stream
.avail_out
;
400 *in
= (const char *) state
->stream
.next_in
;
401 *in_len
= state
->stream
.avail_in
;
406 return TOR_ZLIB_DONE
;
408 if (state
->stream
.avail_in
== 0)
410 return TOR_ZLIB_BUF_FULL
;
412 if (state
->stream
.avail_out
== 0 || finish
)
413 return TOR_ZLIB_BUF_FULL
;
416 log_warn(LD_GENERAL
, "Gzip returned an error: %s",
417 state
->stream
.msg
? state
->stream
.msg
: "<no message>");
422 /** Deallocate <b>state</b>. */
424 tor_zlib_free(tor_zlib_state_t
*state
)
429 deflateEnd(&state
->stream
);
431 inflateEnd(&state
->stream
);