1 /* Copyright 2004 Roger Dingledine */
2 /* Copyright 2004-2006 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 (deflateEnd(stream
)!=Z_OK
) {
140 log_warn(LD_BUG
, "Error freeing gzip structures");
157 /** Given zero or more zlib-compressed or gzip-compressed strings of
159 * <b>in_len</b> bytes at <b>in</b>, uncompress them into a newly allocated
160 * buffer, using the method described in <b>method</b>. Store the uncompressed
161 * string in *<b>out</b>, and its length in *<b>out_len</b>. Return 0 on
162 * success, -1 on failure.
164 * If <b>complete_only</b> is true, we consider a truncated input as a
165 * failure; otherwise we decompress as much as we can. Warn about truncated
166 * or corrupt inputs at <b>protocol_warn_level</b>.
169 tor_gzip_uncompress(char **out
, size_t *out_len
,
170 const char *in
, size_t in_len
,
171 compress_method_t method
,
173 int protocol_warn_level
)
175 struct z_stream_s
*stream
= NULL
;
184 if (method
== GZIP_METHOD
&& !is_gzip_supported()) {
185 /* Old zlib version don't support gzip in inflateInit2 */
186 log_warn(LD_BUG
, "Gzip not supported with zlib %s", ZLIB_VERSION
);
192 stream
= tor_malloc_zero(sizeof(struct z_stream_s
));
193 stream
->zalloc
= Z_NULL
;
194 stream
->zfree
= Z_NULL
;
195 stream
->opaque
= NULL
;
196 stream
->next_in
= (unsigned char*) in
;
197 stream
->avail_in
= in_len
;
199 if (inflateInit2(stream
,
200 method_bits(method
)) != Z_OK
) {
201 log_warn(LD_GENERAL
, "Error from inflateInit2: %s",
202 stream
->msg
?stream
->msg
:"<no message>");
206 out_size
= in_len
* 2; /* guess 50% compression. */
207 if (out_size
< 1024) out_size
= 1024;
209 *out
= tor_malloc(out_size
);
210 stream
->next_out
= (unsigned char*)*out
;
211 stream
->avail_out
= out_size
;
214 switch (inflate(stream
, complete_only
? Z_FINISH
: Z_SYNC_FLUSH
))
217 if (stream
->avail_in
== 0)
219 /* There may be more compressed data here. */
220 if ((r
= inflateEnd(stream
)) != Z_OK
) {
221 log_warn(LD_BUG
, "Error freeing gzip structures");
224 if (inflateInit2(stream
, method_bits(method
)) != Z_OK
) {
225 log_warn(LD_GENERAL
, "Error from second inflateInit2: %s",
226 stream
->msg
?stream
->msg
:"<no message>");
231 if (!complete_only
&& stream
->avail_in
== 0)
233 /* In case zlib doesn't work as I think.... */
234 if (stream
->avail_out
>= stream
->avail_in
+16)
237 if (stream
->avail_out
> 0) {
238 log_fn(protocol_warn_level
, LD_PROTOCOL
,
239 "possible truncated or corrupt zlib data");
242 offset
= stream
->next_out
- (unsigned char*)*out
;
244 *out
= tor_realloc(*out
, out_size
);
245 stream
->next_out
= (unsigned char*)(*out
+ offset
);
246 if (out_size
- offset
> UINT_MAX
) {
247 log_warn(LD_BUG
, "Ran over unsigned int limit of zlib while "
251 stream
->avail_out
= (unsigned int)(out_size
- offset
);
254 log_warn(LD_GENERAL
, "Gzip decompression returned an error: %s",
255 stream
->msg
? stream
->msg
: "<no message>");
260 *out_len
= stream
->next_out
- (unsigned char*)*out
;
261 r
= inflateEnd(stream
);
264 log_warn(LD_BUG
, "Error freeing gzip structures");
268 /* NUL-terminate output. */
269 if (out_size
== *out_len
)
270 *out
= tor_realloc(*out
, out_size
+ 1);
271 (*out
)[*out_len
] = '\0';
285 /** Try to tell whether the <b>in_len</b>-byte string in <b>in</b> is likely
286 * to be compressed or not. If it is, return the likeliest compression method.
287 * Otherwise, return UNKNOWN_METHOD.
290 detect_compression_method(const char *in
, size_t in_len
)
292 if (in_len
> 2 && !memcmp(in
, "\x1f\x8b", 2)) {
294 } else if (in_len
> 2 && (in
[0] & 0x0f) == 8 &&
295 (ntohs(get_uint16(in
)) % 31) == 0) {
298 return UNKNOWN_METHOD
;
302 struct tor_zlib_state_t
{
303 struct z_stream_s stream
;
307 /** Construct and return a tor_zlib_state_t object using <b>method</b>. If
308 * <b>compress</b>, it's for compression; otherwise it's for
311 tor_zlib_new(int compress
, compress_method_t method
)
313 tor_zlib_state_t
*out
;
315 if (method
== GZIP_METHOD
&& !is_gzip_supported()) {
316 /* Old zlib version don't support gzip in inflateInit2 */
317 log_warn(LD_BUG
, "Gzip not supported with zlib %s", ZLIB_VERSION
);
321 out
= tor_malloc_zero(sizeof(tor_zlib_state_t
));
322 out
->stream
.zalloc
= Z_NULL
;
323 out
->stream
.zfree
= Z_NULL
;
324 out
->stream
.opaque
= NULL
;
325 out
->compress
= compress
;
327 if (deflateInit2(&out
->stream
, Z_BEST_COMPRESSION
, Z_DEFLATED
,
328 method_bits(method
), 8, Z_DEFAULT_STRATEGY
) != Z_OK
)
331 if (inflateInit2(&out
->stream
, method_bits(method
)) != Z_OK
)
341 /** Compress/decommpress some bytes using <b>state</b>. Read up to
342 * *<b>in_len</b> bytes from *<b>in</b>, and write up to *<b>out_len</b> bytes
343 * to *<b>out</b>, adjusting the values as we go. If <b>finish</b> is true,
344 * we've reached the end of the input.
346 * Return TOR_ZLIB_DONE if we've finished the entire compression/decompression.
347 * Return TOR_ZLIB_OK if we're processed everything from the input.
348 * Return TOR_ZLIB_BUF_FULL if we're out of space on <b>out</b>.
349 * Return TOR_ZLIB_ERR if the stream is corrupt.
352 tor_zlib_process(tor_zlib_state_t
*state
,
353 char **out
, size_t *out_len
,
354 const char **in
, size_t *in_len
,
358 state
->stream
.next_in
= (unsigned char*) *in
;
359 state
->stream
.avail_in
= *in_len
;
360 state
->stream
.next_out
= (unsigned char*) *out
;
361 state
->stream
.avail_out
= *out_len
;
363 if (state
->compress
) {
364 err
= deflate(&state
->stream
, finish
? Z_FINISH
: Z_SYNC_FLUSH
);
366 err
= inflate(&state
->stream
, finish
? Z_FINISH
: Z_SYNC_FLUSH
);
369 *out
= (char*) state
->stream
.next_out
;
370 *out_len
= state
->stream
.avail_out
;
371 *in
= (const char *) state
->stream
.next_in
;
372 *in_len
= state
->stream
.avail_in
;
377 return TOR_ZLIB_DONE
;
379 if (state
->stream
.avail_in
== 0)
381 return TOR_ZLIB_BUF_FULL
;
383 if (state
->stream
.avail_out
== 0 || finish
)
384 return TOR_ZLIB_BUF_FULL
;
387 log_warn(LD_GENERAL
, "Gzip returned an error: %s",
388 state
->stream
.msg
? state
->stream
.msg
: "<no message>");
393 /** Deallocate <b>state</b>. */
395 tor_zlib_free(tor_zlib_state_t
*state
)
400 deflateEnd(&state
->stream
);
402 inflateEnd(&state
->stream
);