man page entries for TunnelDirConns and PreferTunneledDirConns
[tor.git] / src / common / torgzip.c
blobbadd45be87a40bd676e0c5029525f8e264a1b420
1 /* Copyright 2004 Roger Dingledine */
2 /* Copyright 2004-2006 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 (deflateEnd(stream)!=Z_OK) {
140 log_warn(LD_BUG, "Error freeing gzip structures");
141 goto err;
143 tor_free(stream);
145 return 0;
146 err:
147 if (stream) {
148 deflateEnd(stream);
149 tor_free(stream);
151 if (*out) {
152 tor_free(*out);
154 return -1;
157 /** Given zero or more zlib-compressed or gzip-compressed strings of
158 * total length
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,
172 int complete_only,
173 int protocol_warn_level)
175 struct z_stream_s *stream = NULL;
176 size_t out_size;
177 off_t offset;
178 int r;
180 tor_assert(out);
181 tor_assert(out_len);
182 tor_assert(in);
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);
187 return -1;
190 *out = NULL;
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>");
203 goto err;
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;
213 while (1) {
214 switch (inflate(stream, complete_only ? Z_FINISH : Z_SYNC_FLUSH))
216 case Z_STREAM_END:
217 if (stream->avail_in == 0)
218 goto done;
219 /* There may be more compressed data here. */
220 if ((r = inflateEnd(stream)) != Z_OK) {
221 log_warn(LD_BUG, "Error freeing gzip structures");
222 goto err;
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>");
227 goto err;
229 break;
230 case Z_OK:
231 if (!complete_only && stream->avail_in == 0)
232 goto done;
233 /* In case zlib doesn't work as I think.... */
234 if (stream->avail_out >= stream->avail_in+16)
235 break;
236 case Z_BUF_ERROR:
237 if (stream->avail_out > 0) {
238 log_fn(protocol_warn_level, LD_PROTOCOL,
239 "possible truncated or corrupt zlib data");
240 goto err;
242 offset = stream->next_out - (unsigned char*)*out;
243 out_size *= 2;
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 "
248 "uncompressing.");
249 goto err;
251 stream->avail_out = (unsigned int)(out_size - offset);
252 break;
253 default:
254 log_warn(LD_GENERAL, "Gzip decompression returned an error: %s",
255 stream->msg ? stream->msg : "<no message>");
256 goto err;
259 done:
260 *out_len = stream->next_out - (unsigned char*)*out;
261 r = inflateEnd(stream);
262 tor_free(stream);
263 if (r != Z_OK) {
264 log_warn(LD_BUG, "Error freeing gzip structures");
265 goto err;
268 /* NUL-terminate output. */
269 if (out_size == *out_len)
270 *out = tor_realloc(*out, out_size + 1);
271 (*out)[*out_len] = '\0';
273 return 0;
274 err:
275 if (stream) {
276 inflateEnd(stream);
277 tor_free(stream);
279 if (*out) {
280 tor_free(*out);
282 return -1;
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.
289 compress_method_t
290 detect_compression_method(const char *in, size_t in_len)
292 if (in_len > 2 && !memcmp(in, "\x1f\x8b", 2)) {
293 return GZIP_METHOD;
294 } else if (in_len > 2 && (in[0] & 0x0f) == 8 &&
295 (ntohs(get_uint16(in)) % 31) == 0) {
296 return ZLIB_METHOD;
297 } else {
298 return UNKNOWN_METHOD;
302 struct tor_zlib_state_t {
303 struct z_stream_s stream;
304 int compress;
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
309 * decompression. */
310 tor_zlib_state_t *
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);
318 return NULL;
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;
326 if (compress) {
327 if (deflateInit2(&out->stream, Z_BEST_COMPRESSION, Z_DEFLATED,
328 method_bits(method), 8, Z_DEFAULT_STRATEGY) != Z_OK)
329 goto err;
330 } else {
331 if (inflateInit2(&out->stream, method_bits(method)) != Z_OK)
332 goto err;
334 return out;
336 err:
337 tor_free(out);
338 return NULL;
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.
351 tor_zlib_output_t
352 tor_zlib_process(tor_zlib_state_t *state,
353 char **out, size_t *out_len,
354 const char **in, size_t *in_len,
355 int finish)
357 int err;
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);
365 } else {
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;
374 switch (err)
376 case Z_STREAM_END:
377 return TOR_ZLIB_DONE;
378 case Z_BUF_ERROR:
379 if (state->stream.avail_in == 0)
380 return TOR_ZLIB_OK;
381 return TOR_ZLIB_BUF_FULL;
382 case Z_OK:
383 if (state->stream.avail_out == 0 || finish)
384 return TOR_ZLIB_BUF_FULL;
385 return TOR_ZLIB_OK;
386 default:
387 log_warn(LD_GENERAL, "Gzip returned an error: %s",
388 state->stream.msg ? state->stream.msg : "<no message>");
389 return TOR_ZLIB_ERR;
393 /** Deallocate <b>state</b>. */
394 void
395 tor_zlib_free(tor_zlib_state_t *state)
397 tor_assert(state);
399 if (state->compress)
400 deflateEnd(&state->stream);
401 else
402 inflateEnd(&state->stream);
404 tor_free(state);