Don't stop fetching descriptors when FetchUselessDescriptors is
[tor.git] / src / common / torgzip.c
blobf1771c183c28438506e9486cf32ecc695f222c26
1 /* Copyright 2004 Roger Dingledine */
2 /* Copyright 2004-2007 Roger Dingledine, Nick Mathewson */
3 /* See LICENSE for licensing information */
4 /* $Id$ */
5 const char torgzip_c_id[] =
6 "$Id$";
8 /**
9 * \file torgzip.c
10 * \brief A simple in-memory gzip implementation.
11 **/
13 #include "orconfig.h"
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <assert.h>
18 #ifdef _MSC_VER
19 #include "..\..\contrib\zlib\zlib.h"
20 #else
21 #include <zlib.h>
22 #endif
23 #include <string.h>
24 #ifdef HAVE_NETINET_IN_H
25 #include <netinet/in.h>
26 #endif
28 #include "util.h"
29 #include "log.h"
30 #include "torgzip.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
37 * use zlib. */
38 int
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;
48 else
49 gzip_is_supported = 1;
51 return gzip_is_supported;
54 /** Return the 'bits' value to tell zlib to use <b>method</b>.*/
55 static INLINE int
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.
67 int
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;
73 size_t out_size;
74 off_t offset;
76 tor_assert(out);
77 tor_assert(out_len);
78 tor_assert(in);
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);
83 return -1;
86 *out = NULL;
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,
96 method_bits(method),
97 8, Z_DEFAULT_STRATEGY) != Z_OK) {
98 log_warn(LD_GENERAL, "Error from deflateInit2: %s",
99 stream->msg?stream->msg:"<no message>");
100 goto err;
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;
110 while (1) {
111 switch (deflate(stream, Z_FINISH))
113 case Z_STREAM_END:
114 goto done;
115 case Z_OK:
116 /* In case zlib doesn't work as I think .... */
117 if (stream->avail_out >= stream->avail_in+16)
118 break;
119 case Z_BUF_ERROR:
120 offset = stream->next_out - ((unsigned char*)*out);
121 out_size *= 2;
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 "
126 "uncompressing.");
127 goto err;
129 stream->avail_out = (unsigned int)(out_size - offset);
130 break;
131 default:
132 log_warn(LD_GENERAL, "Gzip compression didn't finish: %s",
133 stream->msg ? stream->msg : "<no message>");
134 goto err;
137 done:
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");
145 goto err;
147 tor_free(stream);
149 return 0;
150 err:
151 if (stream) {
152 deflateEnd(stream);
153 tor_free(stream);
155 if (*out) {
156 tor_free(*out);
158 return -1;
161 /** Given zero or more zlib-compressed or gzip-compressed strings of
162 * total length
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,
176 int complete_only,
177 int protocol_warn_level)
179 struct z_stream_s *stream = NULL;
180 size_t out_size;
181 off_t offset;
182 int r;
184 tor_assert(out);
185 tor_assert(out_len);
186 tor_assert(in);
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);
191 return -1;
194 *out = NULL;
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>");
207 goto err;
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;
217 while (1) {
218 switch (inflate(stream, complete_only ? Z_FINISH : Z_SYNC_FLUSH))
220 case Z_STREAM_END:
221 if (stream->avail_in == 0)
222 goto done;
223 /* There may be more compressed data here. */
224 if ((r = inflateEnd(stream)) != Z_OK) {
225 log_warn(LD_BUG, "Error freeing gzip structures");
226 goto err;
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>");
231 goto err;
233 break;
234 case Z_OK:
235 if (!complete_only && stream->avail_in == 0)
236 goto done;
237 /* In case zlib doesn't work as I think.... */
238 if (stream->avail_out >= stream->avail_in+16)
239 break;
240 case Z_BUF_ERROR:
241 if (stream->avail_out > 0) {
242 log_fn(protocol_warn_level, LD_PROTOCOL,
243 "possible truncated or corrupt zlib data");
244 goto err;
246 offset = stream->next_out - (unsigned char*)*out;
247 out_size *= 2;
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 "
252 "uncompressing.");
253 goto err;
255 stream->avail_out = (unsigned int)(out_size - offset);
256 break;
257 default:
258 log_warn(LD_GENERAL, "Gzip decompression returned an error: %s",
259 stream->msg ? stream->msg : "<no message>");
260 goto err;
263 done:
264 *out_len = stream->next_out - (unsigned char*)*out;
265 r = inflateEnd(stream);
266 tor_free(stream);
267 if (r != Z_OK) {
268 log_warn(LD_BUG, "Error freeing gzip structures");
269 goto err;
272 /* NUL-terminate output. */
273 if (out_size == *out_len)
274 *out = tor_realloc(*out, out_size + 1);
275 (*out)[*out_len] = '\0';
277 return 0;
278 err:
279 if (stream) {
280 inflateEnd(stream);
281 tor_free(stream);
283 if (*out) {
284 tor_free(*out);
286 return -1;
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.
293 compress_method_t
294 detect_compression_method(const char *in, size_t in_len)
296 if (in_len > 2 && !memcmp(in, "\x1f\x8b", 2)) {
297 return GZIP_METHOD;
298 } else if (in_len > 2 && (in[0] & 0x0f) == 8 &&
299 (ntohs(get_uint16(in)) % 31) == 0) {
300 return ZLIB_METHOD;
301 } else {
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;
310 int compress;
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
315 * decompression. */
316 tor_zlib_state_t *
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);
324 return NULL;
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;
332 if (compress) {
333 if (deflateInit2(&out->stream, Z_BEST_COMPRESSION, Z_DEFLATED,
334 method_bits(method), 8, Z_DEFAULT_STRATEGY) != Z_OK)
335 goto err;
336 } else {
337 if (inflateInit2(&out->stream, method_bits(method)) != Z_OK)
338 goto err;
340 return out;
342 err:
343 tor_free(out);
344 return NULL;
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.
357 tor_zlib_output_t
358 tor_zlib_process(tor_zlib_state_t *state,
359 char **out, size_t *out_len,
360 const char **in, size_t *in_len,
361 int finish)
363 int err;
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);
371 } else {
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;
380 switch (err)
382 case Z_STREAM_END:
383 return TOR_ZLIB_DONE;
384 case Z_BUF_ERROR:
385 if (state->stream.avail_in == 0)
386 return TOR_ZLIB_OK;
387 return TOR_ZLIB_BUF_FULL;
388 case Z_OK:
389 if (state->stream.avail_out == 0 || finish)
390 return TOR_ZLIB_BUF_FULL;
391 return TOR_ZLIB_OK;
392 default:
393 log_warn(LD_GENERAL, "Gzip returned an error: %s",
394 state->stream.msg ? state->stream.msg : "<no message>");
395 return TOR_ZLIB_ERR;
399 /** Deallocate <b>state</b>. */
400 void
401 tor_zlib_free(tor_zlib_state_t *state)
403 tor_assert(state);
405 if (state->compress)
406 deflateEnd(&state->stream);
407 else
408 inflateEnd(&state->stream);
410 tor_free(state);