1 /* Copyright 2004 Roger Dingledine */
2 /* Copyright 2004-2007 Roger Dingledine, Nick Mathewson */
3 /* See LICENSE for licensing information */
5 const char torgzip_c_id
[] =
10 * \brief A simple in-memory gzip implementation.
19 #include "..\..\contrib\zlib\zlib.h"
24 #ifdef HAVE_NETINET_IN_H
25 #include <netinet/in.h>
32 /** Set to 1 if zlib is a version that supports gzip; set to 0 if it doesn't;
33 * set to -1 if we haven't checked yet. */
34 static int gzip_is_supported
= -1;
36 /** Return true iff we support gzip-based compression. Otherwise, we need to
39 is_gzip_supported(void)
41 if (gzip_is_supported
>= 0)
42 return gzip_is_supported
;
44 if (!strcmpstart(ZLIB_VERSION
, "0.") ||
45 !strcmpstart(ZLIB_VERSION
, "1.0") ||
46 !strcmpstart(ZLIB_VERSION
, "1.1"))
47 gzip_is_supported
= 0;
49 gzip_is_supported
= 1;
51 return gzip_is_supported
;
54 /** Return the 'bits' value to tell zlib to use <b>method</b>.*/
56 method_bits(compress_method_t method
)
58 /* Bits+16 means "use gzip" in zlib >= 1.2 */
59 return method
== GZIP_METHOD
? 15+16 : 15;
62 /** Given <b>in_len</b> bytes at <b>in</b>, compress them into a newly
63 * allocated buffer, using the method described in <b>method</b>. Store the
64 * compressed string in *<b>out</b>, and its length in *<b>out_len</b>.
65 * Return 0 on success, -1 on failure.
68 tor_gzip_compress(char **out
, size_t *out_len
,
69 const char *in
, size_t in_len
,
70 compress_method_t method
)
72 struct z_stream_s
*stream
= NULL
;
80 if (method
== GZIP_METHOD
&& !is_gzip_supported()) {
81 /* Old zlib version don't support gzip in deflateInit2 */
82 log_warn(LD_BUG
, "Gzip not supported with zlib %s", ZLIB_VERSION
);
88 stream
= tor_malloc_zero(sizeof(struct z_stream_s
));
89 stream
->zalloc
= Z_NULL
;
90 stream
->zfree
= Z_NULL
;
91 stream
->opaque
= NULL
;
92 stream
->next_in
= (unsigned char*) in
;
93 stream
->avail_in
= in_len
;
95 if (deflateInit2(stream
, Z_BEST_COMPRESSION
, Z_DEFLATED
,
97 8, Z_DEFAULT_STRATEGY
) != Z_OK
) {
98 log_warn(LD_GENERAL
, "Error from deflateInit2: %s",
99 stream
->msg
?stream
->msg
:"<no message>");
103 /* Guess 50% compression. */
104 out_size
= in_len
/ 2;
105 if (out_size
< 1024) out_size
= 1024;
106 *out
= tor_malloc(out_size
);
107 stream
->next_out
= (unsigned char*)*out
;
108 stream
->avail_out
= out_size
;
111 switch (deflate(stream
, Z_FINISH
))
116 /* In case zlib doesn't work as I think .... */
117 if (stream
->avail_out
>= stream
->avail_in
+16)
120 offset
= stream
->next_out
- ((unsigned char*)*out
);
122 *out
= tor_realloc(*out
, out_size
);
123 stream
->next_out
= (unsigned char*)(*out
+ offset
);
124 if (out_size
- offset
> UINT_MAX
) {
125 log_warn(LD_BUG
, "Ran over unsigned int limit of zlib while "
129 stream
->avail_out
= (unsigned int)(out_size
- offset
);
132 log_warn(LD_GENERAL
, "Gzip compression didn't finish: %s",
133 stream
->msg
? stream
->msg
: "<no message>");
138 *out_len
= stream
->total_out
;
139 if (stream
->total_out
> out_size
+ 4097) {
140 /* If we're wasting more than 4k, don't. */
141 tor_realloc(*out
, stream
->total_out
+ 1);
143 if (deflateEnd(stream
)!=Z_OK
) {
144 log_warn(LD_BUG
, "Error freeing gzip structures");
161 /** Given zero or more zlib-compressed or gzip-compressed strings of
163 * <b>in_len</b> bytes at <b>in</b>, uncompress them into a newly allocated
164 * buffer, using the method described in <b>method</b>. Store the uncompressed
165 * string in *<b>out</b>, and its length in *<b>out_len</b>. Return 0 on
166 * success, -1 on failure.
168 * If <b>complete_only</b> is true, we consider a truncated input as a
169 * failure; otherwise we decompress as much as we can. Warn about truncated
170 * or corrupt inputs at <b>protocol_warn_level</b>.
173 tor_gzip_uncompress(char **out
, size_t *out_len
,
174 const char *in
, size_t in_len
,
175 compress_method_t method
,
177 int protocol_warn_level
)
179 struct z_stream_s
*stream
= NULL
;
188 if (method
== GZIP_METHOD
&& !is_gzip_supported()) {
189 /* Old zlib version don't support gzip in inflateInit2 */
190 log_warn(LD_BUG
, "Gzip not supported with zlib %s", ZLIB_VERSION
);
196 stream
= tor_malloc_zero(sizeof(struct z_stream_s
));
197 stream
->zalloc
= Z_NULL
;
198 stream
->zfree
= Z_NULL
;
199 stream
->opaque
= NULL
;
200 stream
->next_in
= (unsigned char*) in
;
201 stream
->avail_in
= in_len
;
203 if (inflateInit2(stream
,
204 method_bits(method
)) != Z_OK
) {
205 log_warn(LD_GENERAL
, "Error from inflateInit2: %s",
206 stream
->msg
?stream
->msg
:"<no message>");
210 out_size
= in_len
* 2; /* guess 50% compression. */
211 if (out_size
< 1024) out_size
= 1024;
213 *out
= tor_malloc(out_size
);
214 stream
->next_out
= (unsigned char*)*out
;
215 stream
->avail_out
= out_size
;
218 switch (inflate(stream
, complete_only
? Z_FINISH
: Z_SYNC_FLUSH
))
221 if (stream
->avail_in
== 0)
223 /* There may be more compressed data here. */
224 if ((r
= inflateEnd(stream
)) != Z_OK
) {
225 log_warn(LD_BUG
, "Error freeing gzip structures");
228 if (inflateInit2(stream
, method_bits(method
)) != Z_OK
) {
229 log_warn(LD_GENERAL
, "Error from second inflateInit2: %s",
230 stream
->msg
?stream
->msg
:"<no message>");
235 if (!complete_only
&& stream
->avail_in
== 0)
237 /* In case zlib doesn't work as I think.... */
238 if (stream
->avail_out
>= stream
->avail_in
+16)
241 if (stream
->avail_out
> 0) {
242 log_fn(protocol_warn_level
, LD_PROTOCOL
,
243 "possible truncated or corrupt zlib data");
246 offset
= stream
->next_out
- (unsigned char*)*out
;
248 *out
= tor_realloc(*out
, out_size
);
249 stream
->next_out
= (unsigned char*)(*out
+ offset
);
250 if (out_size
- offset
> UINT_MAX
) {
251 log_warn(LD_BUG
, "Ran over unsigned int limit of zlib while "
255 stream
->avail_out
= (unsigned int)(out_size
- offset
);
258 log_warn(LD_GENERAL
, "Gzip decompression returned an error: %s",
259 stream
->msg
? stream
->msg
: "<no message>");
264 *out_len
= stream
->next_out
- (unsigned char*)*out
;
265 r
= inflateEnd(stream
);
268 log_warn(LD_BUG
, "Error freeing gzip structures");
272 /* NUL-terminate output. */
273 if (out_size
== *out_len
)
274 *out
= tor_realloc(*out
, out_size
+ 1);
275 (*out
)[*out_len
] = '\0';
289 /** Try to tell whether the <b>in_len</b>-byte string in <b>in</b> is likely
290 * to be compressed or not. If it is, return the likeliest compression method.
291 * Otherwise, return UNKNOWN_METHOD.
294 detect_compression_method(const char *in
, size_t in_len
)
296 if (in_len
> 2 && !memcmp(in
, "\x1f\x8b", 2)) {
298 } else if (in_len
> 2 && (in
[0] & 0x0f) == 8 &&
299 (ntohs(get_uint16(in
)) % 31) == 0) {
302 return UNKNOWN_METHOD
;
306 /** Internal state for an incremental zlib compression/decompression. The
307 * body of this struct is not exposed. */
308 struct tor_zlib_state_t
{
309 struct z_stream_s stream
;
313 /** Construct and return a tor_zlib_state_t object using <b>method</b>. If
314 * <b>compress</b>, it's for compression; otherwise it's for
317 tor_zlib_new(int compress
, compress_method_t method
)
319 tor_zlib_state_t
*out
;
321 if (method
== GZIP_METHOD
&& !is_gzip_supported()) {
322 /* Old zlib version don't support gzip in inflateInit2 */
323 log_warn(LD_BUG
, "Gzip not supported with zlib %s", ZLIB_VERSION
);
327 out
= tor_malloc_zero(sizeof(tor_zlib_state_t
));
328 out
->stream
.zalloc
= Z_NULL
;
329 out
->stream
.zfree
= Z_NULL
;
330 out
->stream
.opaque
= NULL
;
331 out
->compress
= compress
;
333 if (deflateInit2(&out
->stream
, Z_BEST_COMPRESSION
, Z_DEFLATED
,
334 method_bits(method
), 8, Z_DEFAULT_STRATEGY
) != Z_OK
)
337 if (inflateInit2(&out
->stream
, method_bits(method
)) != Z_OK
)
347 /** Compress/decommpress some bytes using <b>state</b>. Read up to
348 * *<b>in_len</b> bytes from *<b>in</b>, and write up to *<b>out_len</b> bytes
349 * to *<b>out</b>, adjusting the values as we go. If <b>finish</b> is true,
350 * we've reached the end of the input.
352 * Return TOR_ZLIB_DONE if we've finished the entire compression/decompression.
353 * Return TOR_ZLIB_OK if we're processed everything from the input.
354 * Return TOR_ZLIB_BUF_FULL if we're out of space on <b>out</b>.
355 * Return TOR_ZLIB_ERR if the stream is corrupt.
358 tor_zlib_process(tor_zlib_state_t
*state
,
359 char **out
, size_t *out_len
,
360 const char **in
, size_t *in_len
,
364 state
->stream
.next_in
= (unsigned char*) *in
;
365 state
->stream
.avail_in
= *in_len
;
366 state
->stream
.next_out
= (unsigned char*) *out
;
367 state
->stream
.avail_out
= *out_len
;
369 if (state
->compress
) {
370 err
= deflate(&state
->stream
, finish
? Z_FINISH
: Z_SYNC_FLUSH
);
372 err
= inflate(&state
->stream
, finish
? Z_FINISH
: Z_SYNC_FLUSH
);
375 *out
= (char*) state
->stream
.next_out
;
376 *out_len
= state
->stream
.avail_out
;
377 *in
= (const char *) state
->stream
.next_in
;
378 *in_len
= state
->stream
.avail_in
;
383 return TOR_ZLIB_DONE
;
385 if (state
->stream
.avail_in
== 0)
387 return TOR_ZLIB_BUF_FULL
;
389 if (state
->stream
.avail_out
== 0 || finish
)
390 return TOR_ZLIB_BUF_FULL
;
393 log_warn(LD_GENERAL
, "Gzip returned an error: %s",
394 state
->stream
.msg
? state
->stream
.msg
: "<no message>");
399 /** Deallocate <b>state</b>. */
401 tor_zlib_free(tor_zlib_state_t
*state
)
406 deflateEnd(&state
->stream
);
408 inflateEnd(&state
->stream
);