Forward port changelog
[tor.git] / src / common / torgzip.c
blobe9281ad5ffb4b08bcf4101ee93c22e14e84a732c
1 /* Copyright 2004 Roger Dingledine */
2 /* See LICENSE for licensing information */
3 /* $Id$ */
4 const char torgzip_c_id[] = "$Id$";
6 /**
7 * \file torgzip.c
9 * \brief Simple in-memory gzip implementation.
10 **/
12 #include "orconfig.h"
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <assert.h>
17 #ifdef MS_WINDOWS
18 #include "..\..\contrib\zlib\zlib.h"
19 #else
20 #include <zlib.h>
21 #endif
22 #include <string.h>
23 #ifdef HAVE_NETINET_IN_H
24 #include <netinet/in.h>
25 #endif
27 #include "util.h"
28 #include "log.h"
29 #include "torgzip.h"
31 static int gzip_is_supported = -1;
33 int
34 is_gzip_supported(void)
36 if (gzip_is_supported >= 0)
37 return gzip_is_supported;
39 if (!strcmpstart(ZLIB_VERSION, "0.") ||
40 !strcmpstart(ZLIB_VERSION, "1.0") ||
41 !strcmpstart(ZLIB_VERSION, "1.1"))
42 gzip_is_supported = 0;
43 else
44 gzip_is_supported = 1;
46 return gzip_is_supported;
49 static INLINE int
50 method_bits(compress_method_t method)
52 /* Bits+16 means "use gzip" in zlib >= 1.2 */
53 return method == GZIP_METHOD ? 15+16 : 15;
56 int
57 tor_gzip_compress(char **out, size_t *out_len,
58 const char *in, size_t in_len,
59 compress_method_t method)
61 struct z_stream_s *stream = NULL;
62 size_t out_size;
63 off_t offset;
65 tor_assert(out);
66 tor_assert(out_len);
67 tor_assert(in);
69 if (method == GZIP_METHOD && !is_gzip_supported()) {
70 /* Old zlib version don't support gzip in deflateInit2 */
71 log_fn(LOG_WARN, "Gzip not supported with zlib %s", ZLIB_VERSION);
72 return -1;
75 *out = NULL;
77 stream = tor_malloc_zero(sizeof(struct z_stream_s));
78 stream->zalloc = Z_NULL;
79 stream->zfree = Z_NULL;
80 stream->opaque = NULL;
81 stream->next_in = (unsigned char*) in;
82 stream->avail_in = in_len;
84 if (deflateInit2(stream, Z_BEST_COMPRESSION, Z_DEFLATED,
85 method_bits(method),
86 8, Z_DEFAULT_STRATEGY) != Z_OK) {
87 log_fn(LOG_WARN, "Error from deflateInit2: %s",
88 stream->msg?stream->msg:"<no message>");
89 goto err;
92 /* Guess 50% compression. */
93 out_size = in_len / 2;
94 if (out_size < 1024) out_size = 1024;
95 *out = tor_malloc(out_size);
96 stream->next_out = *out;
97 stream->avail_out = out_size;
99 while (1) {
100 switch (deflate(stream, Z_FINISH))
102 case Z_STREAM_END:
103 goto done;
104 case Z_OK:
105 /* In case zlib doesn't work as I think .... */
106 if (stream->avail_out >= stream->avail_in+16)
107 break;
108 case Z_BUF_ERROR:
109 offset = stream->next_out - ((unsigned char*)*out);
110 out_size *= 2;
111 *out = tor_realloc(*out, out_size);
112 stream->next_out = *out + offset;
113 stream->avail_out = out_size - offset;
114 break;
115 default:
116 log_fn(LOG_WARN, "Gzip compression didn't finish: %s",
117 stream->msg ? stream->msg : "<no message>");
118 goto err;
121 done:
122 *out_len = stream->total_out;
123 if (deflateEnd(stream)!=Z_OK) {
124 log_fn(LOG_WARN, "Error freeing gzip structures");
125 goto err;
127 tor_free(stream);
129 return 0;
130 err:
131 if (stream) {
132 deflateEnd(stream);
133 tor_free(stream);
135 if (*out) {
136 tor_free(*out);
138 return -1;
141 /* DOCDOC -- sets *out to NULL on failure. */
143 tor_gzip_uncompress(char **out, size_t *out_len,
144 const char *in, size_t in_len,
145 compress_method_t method)
147 struct z_stream_s *stream = NULL;
148 size_t out_size;
149 off_t offset;
151 tor_assert(out);
152 tor_assert(out_len);
153 tor_assert(in);
155 if (method == GZIP_METHOD && !is_gzip_supported()) {
156 /* Old zlib version don't support gzip in inflateInit2 */
157 log_fn(LOG_WARN, "Gzip not supported with zlib %s", ZLIB_VERSION);
158 return -1;
161 *out = NULL;
163 stream = tor_malloc_zero(sizeof(struct z_stream_s));
164 stream->zalloc = Z_NULL;
165 stream->zfree = Z_NULL;
166 stream->opaque = NULL;
167 stream->next_in = (unsigned char*) in;
168 stream->avail_in = in_len;
170 if (inflateInit2(stream,
171 method_bits(method)) != Z_OK) {
172 log_fn(LOG_WARN, "Error from inflateInit2: %s",
173 stream->msg?stream->msg:"<no message>");
174 goto err;
177 out_size = in_len * 2; /* guess 50% compression. */
178 if (out_size < 1024) out_size = 1024;
180 *out = tor_malloc(out_size);
181 stream->next_out = *out;
182 stream->avail_out = out_size;
184 while (1) {
185 switch (inflate(stream, Z_FINISH))
187 case Z_STREAM_END:
188 goto done;
189 case Z_OK:
190 /* In case zlib doesn't work as I think.... */
191 if (stream->avail_out >= stream->avail_in+16)
192 break;
193 case Z_BUF_ERROR:
194 offset = stream->next_out - ((unsigned char*)*out);
195 out_size *= 2;
196 *out = tor_realloc(*out, out_size);
197 stream->next_out = *out + offset;
198 stream->avail_out = out_size - offset;
199 break;
200 default:
201 log_fn(LOG_WARN, "Gzip decompression returned an error: %s",
202 stream->msg ? stream->msg : "<no message>");
203 goto err;
207 done:
208 *out_len = stream->total_out;
209 if (inflateEnd(stream)!=Z_OK) {
210 log_fn(LOG_WARN, "Error freeing gzip structures");
211 goto err;
213 tor_free(stream);
215 /* NUL-terminate output. */
216 if (out_size == *out_len)
217 *out = tor_realloc(*out, out_size + 1);
218 (*out)[*out_len] = '\0';
220 return 0;
221 err:
222 if (stream) {
223 inflateEnd(stream);
224 tor_free(stream);
226 if (*out) {
227 tor_free(*out);
229 return -1;
232 /** Try to tell whether the <b>in_len</b>-byte string in <b>in</b> is likely
233 * to be compressed or not. If it is, return the likeliest compression method.
234 * Otherwise, return 0.
236 int detect_compression_method(const char *in, size_t in_len)
238 if (in_len > 2 && !memcmp(in, "\x1f\x8b", 2)) {
239 return GZIP_METHOD;
240 } else if (in_len > 2 && (in[0] & 0x0f) == 8 &&
241 (ntohs(get_uint16(in)) % 31) == 0) {
242 return ZLIB_METHOD;
243 } else {
244 return 0;