1 /* Copyright (c) 2004, Roger Dingledine.
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2010, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
8 * \brief A simple in-memory gzip implementation.
17 #ifdef HAVE_NETINET_IN_H
18 #include <netinet/in.h>
26 /* zlib 1.2.4 and 1.2.5 do some "clever" things with macros. Instead of
27 saying "(defined(FOO) ? FOO : 0)" they like to say "FOO-0", on the theory
28 that nobody will care if the compile outputs a no-such-identifier warning.
30 Sorry, but we like -Werror over here, so I guess we need to define these.
31 I hope that zlib 1.2.6 doesn't break these too.
33 #ifndef _LARGEFILE64_SOURCE
34 #define _LARGEFILE64_SOURCE 0
36 #ifndef _LFS64_LARGEFILE
37 #define _LFS64_LARGEFILE 0
39 #ifndef _FILE_OFFSET_BITS
40 #define _FILE_OFFSET_BITS 0
43 #define off64_t int64_t
47 #include "..\..\contrib\zlib\zlib.h"
52 /** Set to 1 if zlib is a version that supports gzip; set to 0 if it doesn't;
53 * set to -1 if we haven't checked yet. */
54 static int gzip_is_supported
= -1;
56 /** Return true iff we support gzip-based compression. Otherwise, we need to
59 is_gzip_supported(void)
61 if (gzip_is_supported
>= 0)
62 return gzip_is_supported
;
64 if (!strcmpstart(ZLIB_VERSION
, "0.") ||
65 !strcmpstart(ZLIB_VERSION
, "1.0") ||
66 !strcmpstart(ZLIB_VERSION
, "1.1"))
67 gzip_is_supported
= 0;
69 gzip_is_supported
= 1;
71 return gzip_is_supported
;
74 /** Return the 'bits' value to tell zlib to use <b>method</b>.*/
76 method_bits(compress_method_t method
)
78 /* Bits+16 means "use gzip" in zlib >= 1.2 */
79 return method
== GZIP_METHOD
? 15+16 : 15;
82 /** Given <b>in_len</b> bytes at <b>in</b>, compress them into a newly
83 * allocated buffer, using the method described in <b>method</b>. Store the
84 * compressed string in *<b>out</b>, and its length in *<b>out_len</b>.
85 * Return 0 on success, -1 on failure.
88 tor_gzip_compress(char **out
, size_t *out_len
,
89 const char *in
, size_t in_len
,
90 compress_method_t method
)
92 struct z_stream_s
*stream
= NULL
;
93 size_t out_size
, old_size
;
99 tor_assert(in_len
< UINT_MAX
);
103 if (method
== GZIP_METHOD
&& !is_gzip_supported()) {
104 /* Old zlib version don't support gzip in deflateInit2 */
105 log_warn(LD_BUG
, "Gzip not supported with zlib %s", ZLIB_VERSION
);
109 stream
= tor_malloc_zero(sizeof(struct z_stream_s
));
110 stream
->zalloc
= Z_NULL
;
111 stream
->zfree
= Z_NULL
;
112 stream
->opaque
= NULL
;
113 stream
->next_in
= (unsigned char*) in
;
114 stream
->avail_in
= (unsigned int)in_len
;
116 if (deflateInit2(stream
, Z_BEST_COMPRESSION
, Z_DEFLATED
,
118 8, Z_DEFAULT_STRATEGY
) != Z_OK
) {
119 log_warn(LD_GENERAL
, "Error from deflateInit2: %s",
120 stream
->msg
?stream
->msg
:"<no message>");
124 /* Guess 50% compression. */
125 out_size
= in_len
/ 2;
126 if (out_size
< 1024) out_size
= 1024;
127 *out
= tor_malloc(out_size
);
128 stream
->next_out
= (unsigned char*)*out
;
129 stream
->avail_out
= (unsigned int)out_size
;
132 switch (deflate(stream
, Z_FINISH
))
137 /* In case zlib doesn't work as I think .... */
138 if (stream
->avail_out
>= stream
->avail_in
+16)
141 offset
= stream
->next_out
- ((unsigned char*)*out
);
144 if (out_size
< old_size
) {
145 log_warn(LD_GENERAL
, "Size overflow in compression.");
148 *out
= tor_realloc(*out
, out_size
);
149 stream
->next_out
= (unsigned char*)(*out
+ offset
);
150 if (out_size
- offset
> UINT_MAX
) {
151 log_warn(LD_BUG
, "Ran over unsigned int limit of zlib while "
155 stream
->avail_out
= (unsigned int)(out_size
- offset
);
158 log_warn(LD_GENERAL
, "Gzip compression didn't finish: %s",
159 stream
->msg
? stream
->msg
: "<no message>");
164 *out_len
= stream
->total_out
;
166 /* "Hey Rocky! Watch me change an unsigned field to a signed field in a
168 * "Oh, that trick will just make people do unsafe casts to the unsigned
169 * type in their cross-platform code!"
170 * "Don't be foolish. I'm _sure_ they'll have the good sense to make sure
171 * the newly unsigned field isn't negative." */
172 tor_assert(stream
->total_out
>= 0);
174 if (((size_t)stream
->total_out
) > out_size
+ 4097) {
175 /* If we're wasting more than 4k, don't. */
176 *out
= tor_realloc(*out
, stream
->total_out
+ 1);
178 if (deflateEnd(stream
)!=Z_OK
) {
179 log_warn(LD_BUG
, "Error freeing gzip structures");
194 /** Given zero or more zlib-compressed or gzip-compressed strings of
196 * <b>in_len</b> bytes at <b>in</b>, uncompress them into a newly allocated
197 * buffer, using the method described in <b>method</b>. Store the uncompressed
198 * string in *<b>out</b>, and its length in *<b>out_len</b>. Return 0 on
199 * success, -1 on failure.
201 * If <b>complete_only</b> is true, we consider a truncated input as a
202 * failure; otherwise we decompress as much as we can. Warn about truncated
203 * or corrupt inputs at <b>protocol_warn_level</b>.
206 tor_gzip_uncompress(char **out
, size_t *out_len
,
207 const char *in
, size_t in_len
,
208 compress_method_t method
,
210 int protocol_warn_level
)
212 struct z_stream_s
*stream
= NULL
;
213 size_t out_size
, old_size
;
220 tor_assert(in_len
< UINT_MAX
);
222 if (method
== GZIP_METHOD
&& !is_gzip_supported()) {
223 /* Old zlib version don't support gzip in inflateInit2 */
224 log_warn(LD_BUG
, "Gzip not supported with zlib %s", ZLIB_VERSION
);
230 stream
= tor_malloc_zero(sizeof(struct z_stream_s
));
231 stream
->zalloc
= Z_NULL
;
232 stream
->zfree
= Z_NULL
;
233 stream
->opaque
= NULL
;
234 stream
->next_in
= (unsigned char*) in
;
235 stream
->avail_in
= (unsigned int)in_len
;
237 if (inflateInit2(stream
,
238 method_bits(method
)) != Z_OK
) {
239 log_warn(LD_GENERAL
, "Error from inflateInit2: %s",
240 stream
->msg
?stream
->msg
:"<no message>");
244 out_size
= in_len
* 2; /* guess 50% compression. */
245 if (out_size
< 1024) out_size
= 1024;
246 if (out_size
> UINT_MAX
)
249 *out
= tor_malloc(out_size
);
250 stream
->next_out
= (unsigned char*)*out
;
251 stream
->avail_out
= (unsigned int)out_size
;
254 switch (inflate(stream
, complete_only
? Z_FINISH
: Z_SYNC_FLUSH
))
257 if (stream
->avail_in
== 0)
259 /* There may be more compressed data here. */
260 if ((r
= inflateEnd(stream
)) != Z_OK
) {
261 log_warn(LD_BUG
, "Error freeing gzip structures");
264 if (inflateInit2(stream
, method_bits(method
)) != Z_OK
) {
265 log_warn(LD_GENERAL
, "Error from second inflateInit2: %s",
266 stream
->msg
?stream
->msg
:"<no message>");
271 if (!complete_only
&& stream
->avail_in
== 0)
273 /* In case zlib doesn't work as I think.... */
274 if (stream
->avail_out
>= stream
->avail_in
+16)
277 if (stream
->avail_out
> 0) {
278 log_fn(protocol_warn_level
, LD_PROTOCOL
,
279 "possible truncated or corrupt zlib data");
282 offset
= stream
->next_out
- (unsigned char*)*out
;
285 if (out_size
< old_size
) {
286 log_warn(LD_GENERAL
, "Size overflow in compression.");
289 *out
= tor_realloc(*out
, out_size
);
290 stream
->next_out
= (unsigned char*)(*out
+ offset
);
291 if (out_size
- offset
> UINT_MAX
) {
292 log_warn(LD_BUG
, "Ran over unsigned int limit of zlib while "
296 stream
->avail_out
= (unsigned int)(out_size
- offset
);
299 log_warn(LD_GENERAL
, "Gzip decompression returned an error: %s",
300 stream
->msg
? stream
->msg
: "<no message>");
305 *out_len
= stream
->next_out
- (unsigned char*)*out
;
306 r
= inflateEnd(stream
);
309 log_warn(LD_BUG
, "Error freeing gzip structures");
313 /* NUL-terminate output. */
314 if (out_size
== *out_len
)
315 *out
= tor_realloc(*out
, out_size
+ 1);
316 (*out
)[*out_len
] = '\0';
330 /** Try to tell whether the <b>in_len</b>-byte string in <b>in</b> is likely
331 * to be compressed or not. If it is, return the likeliest compression method.
332 * Otherwise, return UNKNOWN_METHOD.
335 detect_compression_method(const char *in
, size_t in_len
)
337 if (in_len
> 2 && !memcmp(in
, "\x1f\x8b", 2)) {
339 } else if (in_len
> 2 && (in
[0] & 0x0f) == 8 &&
340 (ntohs(get_uint16(in
)) % 31) == 0) {
343 return UNKNOWN_METHOD
;
347 /** Internal state for an incremental zlib compression/decompression. The
348 * body of this struct is not exposed. */
349 struct tor_zlib_state_t
{
350 struct z_stream_s stream
;
354 /** Construct and return a tor_zlib_state_t object using <b>method</b>. If
355 * <b>compress</b>, it's for compression; otherwise it's for
358 tor_zlib_new(int compress
, compress_method_t method
)
360 tor_zlib_state_t
*out
;
362 if (method
== GZIP_METHOD
&& !is_gzip_supported()) {
363 /* Old zlib version don't support gzip in inflateInit2 */
364 log_warn(LD_BUG
, "Gzip not supported with zlib %s", ZLIB_VERSION
);
368 out
= tor_malloc_zero(sizeof(tor_zlib_state_t
));
369 out
->stream
.zalloc
= Z_NULL
;
370 out
->stream
.zfree
= Z_NULL
;
371 out
->stream
.opaque
= NULL
;
372 out
->compress
= compress
;
374 if (deflateInit2(&out
->stream
, Z_BEST_COMPRESSION
, Z_DEFLATED
,
375 method_bits(method
), 8, Z_DEFAULT_STRATEGY
) != Z_OK
)
378 if (inflateInit2(&out
->stream
, method_bits(method
)) != Z_OK
)
388 /** Compress/decompress some bytes using <b>state</b>. Read up to
389 * *<b>in_len</b> bytes from *<b>in</b>, and write up to *<b>out_len</b> bytes
390 * to *<b>out</b>, adjusting the values as we go. If <b>finish</b> is true,
391 * we've reached the end of the input.
393 * Return TOR_ZLIB_DONE if we've finished the entire compression/decompression.
394 * Return TOR_ZLIB_OK if we're processed everything from the input.
395 * Return TOR_ZLIB_BUF_FULL if we're out of space on <b>out</b>.
396 * Return TOR_ZLIB_ERR if the stream is corrupt.
399 tor_zlib_process(tor_zlib_state_t
*state
,
400 char **out
, size_t *out_len
,
401 const char **in
, size_t *in_len
,
405 tor_assert(*in_len
<= UINT_MAX
);
406 tor_assert(*out_len
<= UINT_MAX
);
407 state
->stream
.next_in
= (unsigned char*) *in
;
408 state
->stream
.avail_in
= (unsigned int)*in_len
;
409 state
->stream
.next_out
= (unsigned char*) *out
;
410 state
->stream
.avail_out
= (unsigned int)*out_len
;
412 if (state
->compress
) {
413 err
= deflate(&state
->stream
, finish
? Z_FINISH
: Z_SYNC_FLUSH
);
415 err
= inflate(&state
->stream
, finish
? Z_FINISH
: Z_SYNC_FLUSH
);
418 *out
= (char*) state
->stream
.next_out
;
419 *out_len
= state
->stream
.avail_out
;
420 *in
= (const char *) state
->stream
.next_in
;
421 *in_len
= state
->stream
.avail_in
;
426 return TOR_ZLIB_DONE
;
428 if (state
->stream
.avail_in
== 0)
430 return TOR_ZLIB_BUF_FULL
;
432 if (state
->stream
.avail_out
== 0 || finish
)
433 return TOR_ZLIB_BUF_FULL
;
436 log_warn(LD_GENERAL
, "Gzip returned an error: %s",
437 state
->stream
.msg
? state
->stream
.msg
: "<no message>");
442 /** Deallocate <b>state</b>. */
444 tor_zlib_free(tor_zlib_state_t
*state
)
450 deflateEnd(&state
->stream
);
452 inflateEnd(&state
->stream
);