update copyright notices.
[tor.git] / src / common / torgzip.c
blob23d5dd5a588b7cb9c71cb5ece518173adfeec3e8
1 /* Copyright 2004 Roger Dingledine */
2 /* Copyright 2004-2005 Roger Dingledine, Nick Mathewson */
3 /* See LICENSE for licensing information */
4 /* $Id$ */
5 const char torgzip_c_id[] = "$Id$";
7 /**
8 * \file torgzip.c
10 * \brief Simple in-memory gzip implementation.
11 **/
13 #include "orconfig.h"
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <assert.h>
18 #ifdef MS_WINDOWS
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 static int gzip_is_supported = -1;
34 int
35 is_gzip_supported(void)
37 if (gzip_is_supported >= 0)
38 return gzip_is_supported;
40 if (!strcmpstart(ZLIB_VERSION, "0.") ||
41 !strcmpstart(ZLIB_VERSION, "1.0") ||
42 !strcmpstart(ZLIB_VERSION, "1.1"))
43 gzip_is_supported = 0;
44 else
45 gzip_is_supported = 1;
47 return gzip_is_supported;
50 static INLINE int
51 method_bits(compress_method_t method)
53 /* Bits+16 means "use gzip" in zlib >= 1.2 */
54 return method == GZIP_METHOD ? 15+16 : 15;
57 int
58 tor_gzip_compress(char **out, size_t *out_len,
59 const char *in, size_t in_len,
60 compress_method_t method)
62 struct z_stream_s *stream = NULL;
63 size_t out_size;
64 off_t offset;
66 tor_assert(out);
67 tor_assert(out_len);
68 tor_assert(in);
70 if (method == GZIP_METHOD && !is_gzip_supported()) {
71 /* Old zlib version don't support gzip in deflateInit2 */
72 log_fn(LOG_WARN, "Gzip not supported with zlib %s", ZLIB_VERSION);
73 return -1;
76 *out = NULL;
78 stream = tor_malloc_zero(sizeof(struct z_stream_s));
79 stream->zalloc = Z_NULL;
80 stream->zfree = Z_NULL;
81 stream->opaque = NULL;
82 stream->next_in = (unsigned char*) in;
83 stream->avail_in = in_len;
85 if (deflateInit2(stream, Z_BEST_COMPRESSION, Z_DEFLATED,
86 method_bits(method),
87 8, Z_DEFAULT_STRATEGY) != Z_OK) {
88 log_fn(LOG_WARN, "Error from deflateInit2: %s",
89 stream->msg?stream->msg:"<no message>");
90 goto err;
93 /* Guess 50% compression. */
94 out_size = in_len / 2;
95 if (out_size < 1024) out_size = 1024;
96 *out = tor_malloc(out_size);
97 stream->next_out = *out;
98 stream->avail_out = out_size;
100 while (1) {
101 switch (deflate(stream, Z_FINISH))
103 case Z_STREAM_END:
104 goto done;
105 case Z_OK:
106 /* In case zlib doesn't work as I think .... */
107 if (stream->avail_out >= stream->avail_in+16)
108 break;
109 case Z_BUF_ERROR:
110 offset = stream->next_out - ((unsigned char*)*out);
111 out_size *= 2;
112 *out = tor_realloc(*out, out_size);
113 stream->next_out = *out + offset;
114 stream->avail_out = out_size - offset;
115 break;
116 default:
117 log_fn(LOG_WARN, "Gzip compression didn't finish: %s",
118 stream->msg ? stream->msg : "<no message>");
119 goto err;
122 done:
123 *out_len = stream->total_out;
124 if (deflateEnd(stream)!=Z_OK) {
125 log_fn(LOG_WARN, "Error freeing gzip structures");
126 goto err;
128 tor_free(stream);
130 return 0;
131 err:
132 if (stream) {
133 deflateEnd(stream);
134 tor_free(stream);
136 if (*out) {
137 tor_free(*out);
139 return -1;
142 /* DOCDOC -- sets *out to NULL on failure. */
144 tor_gzip_uncompress(char **out, size_t *out_len,
145 const char *in, size_t in_len,
146 compress_method_t method)
148 struct z_stream_s *stream = NULL;
149 size_t out_size;
150 off_t offset;
152 tor_assert(out);
153 tor_assert(out_len);
154 tor_assert(in);
156 if (method == GZIP_METHOD && !is_gzip_supported()) {
157 /* Old zlib version don't support gzip in inflateInit2 */
158 log_fn(LOG_WARN, "Gzip not supported with zlib %s", ZLIB_VERSION);
159 return -1;
162 *out = NULL;
164 stream = tor_malloc_zero(sizeof(struct z_stream_s));
165 stream->zalloc = Z_NULL;
166 stream->zfree = Z_NULL;
167 stream->opaque = NULL;
168 stream->next_in = (unsigned char*) in;
169 stream->avail_in = in_len;
171 if (inflateInit2(stream,
172 method_bits(method)) != Z_OK) {
173 log_fn(LOG_WARN, "Error from inflateInit2: %s",
174 stream->msg?stream->msg:"<no message>");
175 goto err;
178 out_size = in_len * 2; /* guess 50% compression. */
179 if (out_size < 1024) out_size = 1024;
181 *out = tor_malloc(out_size);
182 stream->next_out = *out;
183 stream->avail_out = out_size;
185 while (1) {
186 switch (inflate(stream, Z_FINISH))
188 case Z_STREAM_END:
189 goto done;
190 case Z_OK:
191 /* In case zlib doesn't work as I think.... */
192 if (stream->avail_out >= stream->avail_in+16)
193 break;
194 case Z_BUF_ERROR:
195 offset = stream->next_out - ((unsigned char*)*out);
196 out_size *= 2;
197 *out = tor_realloc(*out, out_size);
198 stream->next_out = *out + offset;
199 stream->avail_out = out_size - offset;
200 break;
201 default:
202 log_fn(LOG_WARN, "Gzip decompression returned an error: %s",
203 stream->msg ? stream->msg : "<no message>");
204 goto err;
208 done:
209 *out_len = stream->total_out;
210 if (inflateEnd(stream)!=Z_OK) {
211 log_fn(LOG_WARN, "Error freeing gzip structures");
212 goto err;
214 tor_free(stream);
216 /* NUL-terminate output. */
217 if (out_size == *out_len)
218 *out = tor_realloc(*out, out_size + 1);
219 (*out)[*out_len] = '\0';
221 return 0;
222 err:
223 if (stream) {
224 inflateEnd(stream);
225 tor_free(stream);
227 if (*out) {
228 tor_free(*out);
230 return -1;
233 /** Try to tell whether the <b>in_len</b>-byte string in <b>in</b> is likely
234 * to be compressed or not. If it is, return the likeliest compression method.
235 * Otherwise, return 0.
237 int detect_compression_method(const char *in, size_t in_len)
239 if (in_len > 2 && !memcmp(in, "\x1f\x8b", 2)) {
240 return GZIP_METHOD;
241 } else if (in_len > 2 && (in[0] & 0x0f) == 8 &&
242 (ntohs(get_uint16(in)) % 31) == 0) {
243 return ZLIB_METHOD;
244 } else {
245 return 0;