torgzip.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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$";
  6. /**
  7. * \file torgzip.c
  8. * \brief A simple in-memory gzip implementation.
  9. **/
  10. #include "orconfig.h"
  11. #include <stdlib.h>
  12. #include <stdio.h>
  13. #include <assert.h>
  14. #ifdef MS_WINDOWS
  15. #include "..\..\contrib\zlib\zlib.h"
  16. #else
  17. #include <zlib.h>
  18. #endif
  19. #include <string.h>
  20. #ifdef HAVE_NETINET_IN_H
  21. #include <netinet/in.h>
  22. #endif
  23. #include "util.h"
  24. #include "log.h"
  25. #include "torgzip.h"
  26. static int gzip_is_supported = -1;
  27. int
  28. is_gzip_supported(void)
  29. {
  30. if (gzip_is_supported >= 0)
  31. return gzip_is_supported;
  32. if (!strcmpstart(ZLIB_VERSION, "0.") ||
  33. !strcmpstart(ZLIB_VERSION, "1.0") ||
  34. !strcmpstart(ZLIB_VERSION, "1.1"))
  35. gzip_is_supported = 0;
  36. else
  37. gzip_is_supported = 1;
  38. return gzip_is_supported;
  39. }
  40. static INLINE int
  41. method_bits(compress_method_t method)
  42. {
  43. /* Bits+16 means "use gzip" in zlib >= 1.2 */
  44. return method == GZIP_METHOD ? 15+16 : 15;
  45. }
  46. int
  47. tor_gzip_compress(char **out, size_t *out_len,
  48. const char *in, size_t in_len,
  49. compress_method_t method)
  50. {
  51. struct z_stream_s *stream = NULL;
  52. size_t out_size;
  53. off_t offset;
  54. tor_assert(out);
  55. tor_assert(out_len);
  56. tor_assert(in);
  57. if (method == GZIP_METHOD && !is_gzip_supported()) {
  58. /* Old zlib version don't support gzip in deflateInit2 */
  59. log_fn(LOG_WARN, "Gzip not supported with zlib %s", ZLIB_VERSION);
  60. return -1;
  61. }
  62. *out = NULL;
  63. stream = tor_malloc_zero(sizeof(struct z_stream_s));
  64. stream->zalloc = Z_NULL;
  65. stream->zfree = Z_NULL;
  66. stream->opaque = NULL;
  67. stream->next_in = (unsigned char*) in;
  68. stream->avail_in = in_len;
  69. if (deflateInit2(stream, Z_BEST_COMPRESSION, Z_DEFLATED,
  70. method_bits(method),
  71. 8, Z_DEFAULT_STRATEGY) != Z_OK) {
  72. log_fn(LOG_WARN, "Error from deflateInit2: %s",
  73. stream->msg?stream->msg:"<no message>");
  74. goto err;
  75. }
  76. /* Guess 50% compression. */
  77. out_size = in_len / 2;
  78. if (out_size < 1024) out_size = 1024;
  79. *out = tor_malloc(out_size);
  80. stream->next_out = (unsigned char*)*out;
  81. stream->avail_out = out_size;
  82. while (1) {
  83. switch (deflate(stream, Z_FINISH))
  84. {
  85. case Z_STREAM_END:
  86. goto done;
  87. case Z_OK:
  88. /* In case zlib doesn't work as I think .... */
  89. if (stream->avail_out >= stream->avail_in+16)
  90. break;
  91. case Z_BUF_ERROR:
  92. offset = stream->next_out - ((unsigned char*)*out);
  93. out_size *= 2;
  94. *out = tor_realloc(*out, out_size);
  95. stream->next_out = (unsigned char*)(*out + offset);
  96. stream->avail_out = out_size - offset;
  97. break;
  98. default:
  99. log_fn(LOG_WARN, "Gzip compression didn't finish: %s",
  100. stream->msg ? stream->msg : "<no message>");
  101. goto err;
  102. }
  103. }
  104. done:
  105. *out_len = stream->total_out;
  106. if (deflateEnd(stream)!=Z_OK) {
  107. log_fn(LOG_WARN, "Error freeing gzip structures");
  108. goto err;
  109. }
  110. tor_free(stream);
  111. return 0;
  112. err:
  113. if (stream) {
  114. deflateEnd(stream);
  115. tor_free(stream);
  116. }
  117. if (*out) {
  118. tor_free(*out);
  119. }
  120. return -1;
  121. }
  122. /* DOCDOC -- sets *out to NULL on failure. */
  123. int
  124. tor_gzip_uncompress(char **out, size_t *out_len,
  125. const char *in, size_t in_len,
  126. compress_method_t method)
  127. {
  128. struct z_stream_s *stream = NULL;
  129. size_t out_size;
  130. off_t offset;
  131. int r;
  132. tor_assert(out);
  133. tor_assert(out_len);
  134. tor_assert(in);
  135. if (method == GZIP_METHOD && !is_gzip_supported()) {
  136. /* Old zlib version don't support gzip in inflateInit2 */
  137. log_fn(LOG_WARN, "Gzip not supported with zlib %s", ZLIB_VERSION);
  138. return -1;
  139. }
  140. *out = NULL;
  141. stream = tor_malloc_zero(sizeof(struct z_stream_s));
  142. stream->zalloc = Z_NULL;
  143. stream->zfree = Z_NULL;
  144. stream->opaque = NULL;
  145. stream->next_in = (unsigned char*) in;
  146. stream->avail_in = in_len;
  147. if (inflateInit2(stream,
  148. method_bits(method)) != Z_OK) {
  149. log_fn(LOG_WARN, "Error from inflateInit2: %s",
  150. stream->msg?stream->msg:"<no message>");
  151. goto err;
  152. }
  153. out_size = in_len * 2; /* guess 50% compression. */
  154. if (out_size < 1024) out_size = 1024;
  155. *out = tor_malloc(out_size);
  156. stream->next_out = (unsigned char*)*out;
  157. stream->avail_out = out_size;
  158. while (1) {
  159. switch (inflate(stream, Z_FINISH))
  160. {
  161. case Z_STREAM_END:
  162. goto done;
  163. case Z_OK:
  164. /* In case zlib doesn't work as I think.... */
  165. if (stream->avail_out >= stream->avail_in+16)
  166. break;
  167. case Z_BUF_ERROR:
  168. offset = stream->next_out - ((unsigned char*)*out);
  169. out_size *= 2;
  170. *out = tor_realloc(*out, out_size);
  171. stream->next_out = (unsigned char*)(*out + offset);
  172. stream->avail_out = out_size - offset;
  173. break;
  174. default:
  175. log_fn(LOG_WARN, "Gzip decompression returned an error: %s",
  176. stream->msg ? stream->msg : "<no message>");
  177. goto err;
  178. }
  179. }
  180. done:
  181. *out_len = stream->total_out;
  182. r = inflateEnd(stream);
  183. tor_free(stream);
  184. if (r != Z_OK) {
  185. log_fn(LOG_WARN, "Error freeing gzip structures");
  186. goto err;
  187. }
  188. /* NUL-terminate output. */
  189. if (out_size == *out_len)
  190. *out = tor_realloc(*out, out_size + 1);
  191. (*out)[*out_len] = '\0';
  192. return 0;
  193. err:
  194. if (stream) {
  195. inflateEnd(stream);
  196. tor_free(stream);
  197. }
  198. if (*out) {
  199. tor_free(*out);
  200. }
  201. return -1;
  202. }
  203. /** Try to tell whether the <b>in_len</b>-byte string in <b>in</b> is likely
  204. * to be compressed or not. If it is, return the likeliest compression method.
  205. * Otherwise, return 0.
  206. */
  207. int detect_compression_method(const char *in, size_t in_len)
  208. {
  209. if (in_len > 2 && !memcmp(in, "\x1f\x8b", 2)) {
  210. return GZIP_METHOD;
  211. } else if (in_len > 2 && (in[0] & 0x0f) == 8 &&
  212. (ntohs(get_uint16(in)) % 31) == 0) {
  213. return ZLIB_METHOD;
  214. } else {
  215. return 0;
  216. }
  217. }