torgzip.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. /* Copyright 2004 Roger Dingledine */
  2. /* See LICENSE for licensing information */
  3. /* $Id$ */
  4. const char torgzip_c_id[] = "$Id$";
  5. /**
  6. * \file torgzip.c
  7. *
  8. * \brief 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 "util.h"
  20. #include "log.h"
  21. #include "torgzip.h"
  22. static int gzip_is_supported = -1;
  23. int
  24. is_gzip_supported(void)
  25. {
  26. if (gzip_is_supported >= 0)
  27. return gzip_is_supported;
  28. if (!strcmpstart(ZLIB_VERSION, "0.") ||
  29. !strcmpstart(ZLIB_VERSION, "1.0") ||
  30. !strcmpstart(ZLIB_VERSION, "1.1"))
  31. gzip_is_supported = 0;
  32. else
  33. gzip_is_supported = 1;
  34. return gzip_is_supported;
  35. }
  36. static INLINE int
  37. method_bits(compress_method_t method)
  38. {
  39. /* Bits+16 means "use gzip" in zlib >= 1.2 */
  40. return method == GZIP_METHOD ? 15+16 : 15;
  41. }
  42. int
  43. tor_gzip_compress(char **out, size_t *out_len,
  44. const char *in, size_t in_len,
  45. compress_method_t method)
  46. {
  47. struct z_stream_s *stream = NULL;
  48. size_t out_size;
  49. off_t offset;
  50. tor_assert(out);
  51. tor_assert(out_len);
  52. tor_assert(in);
  53. if (method == GZIP_METHOD && !is_gzip_supported()) {
  54. /* Old zlib version don't support gzip in deflateInit2 */
  55. log_fn(LOG_WARN, "Gzip not supported with zlib %s", ZLIB_VERSION);
  56. return -1;
  57. }
  58. *out = NULL;
  59. stream = tor_malloc_zero(sizeof(struct z_stream_s));
  60. stream->zalloc = Z_NULL;
  61. stream->zfree = Z_NULL;
  62. stream->opaque = NULL;
  63. stream->next_in = (unsigned char*) in;
  64. stream->avail_in = in_len;
  65. if (deflateInit2(stream, Z_BEST_COMPRESSION, Z_DEFLATED,
  66. method_bits(method),
  67. 8, Z_DEFAULT_STRATEGY) != Z_OK) {
  68. log_fn(LOG_WARN, "Error from deflateInit2: %s",
  69. stream->msg?stream->msg:"<no message>");
  70. goto err;
  71. }
  72. /* Guess 50% compression. */
  73. out_size = in_len / 2;
  74. if (out_size < 1024) out_size = 1024;
  75. *out = tor_malloc(out_size);
  76. stream->next_out = *out;
  77. stream->avail_out = out_size;
  78. while (1) {
  79. switch (deflate(stream, Z_FINISH))
  80. {
  81. case Z_STREAM_END:
  82. goto done;
  83. case Z_OK:
  84. /* In case zlib doesn't work as I think .... */
  85. if (stream->avail_out >= stream->avail_in+16)
  86. break;
  87. case Z_BUF_ERROR:
  88. offset = stream->next_out - ((unsigned char*)*out);
  89. out_size *= 2;
  90. *out = tor_realloc(*out, out_size);
  91. stream->next_out = *out + offset;
  92. stream->avail_out = out_size - offset;
  93. break;
  94. default:
  95. log_fn(LOG_WARN, "Gzip compression didn't finish: %s",
  96. stream->msg ? stream->msg : "<no message>");
  97. goto err;
  98. }
  99. }
  100. done:
  101. *out_len = stream->total_out;
  102. if (deflateEnd(stream)!=Z_OK) {
  103. log_fn(LOG_WARN, "Error freeing gzip structures");
  104. goto err;
  105. }
  106. tor_free(stream);
  107. return 0;
  108. err:
  109. if (stream) {
  110. deflateEnd(stream);
  111. tor_free(stream);
  112. }
  113. if (*out) {
  114. tor_free(*out);
  115. }
  116. return -1;
  117. }
  118. int
  119. tor_gzip_uncompress(char **out, size_t *out_len,
  120. const char *in, size_t in_len,
  121. compress_method_t method)
  122. {
  123. struct z_stream_s *stream = NULL;
  124. size_t out_size;
  125. off_t offset;
  126. tor_assert(out);
  127. tor_assert(out_len);
  128. tor_assert(in);
  129. if (method == GZIP_METHOD && !is_gzip_supported()) {
  130. /* Old zlib version don't support gzip in inflateInit2 */
  131. log_fn(LOG_WARN, "Gzip not supported with zlib %s", ZLIB_VERSION);
  132. return -1;
  133. }
  134. *out = NULL;
  135. stream = tor_malloc_zero(sizeof(struct z_stream_s));
  136. stream->zalloc = Z_NULL;
  137. stream->zfree = Z_NULL;
  138. stream->opaque = NULL;
  139. stream->next_in = (unsigned char*) in;
  140. stream->avail_in = in_len;
  141. if (inflateInit2(stream,
  142. method_bits(method)) != Z_OK) {
  143. log_fn(LOG_WARN, "Error from inflateInit2: %s",
  144. stream->msg?stream->msg:"<no message>");
  145. goto err;
  146. }
  147. out_size = in_len * 2; /* guess 50% compression. */
  148. if (out_size < 1024) out_size = 1024;
  149. *out = tor_malloc(out_size);
  150. stream->next_out = *out;
  151. stream->avail_out = out_size;
  152. while (1) {
  153. switch (inflate(stream, Z_FINISH))
  154. {
  155. case Z_STREAM_END:
  156. goto done;
  157. case Z_OK:
  158. /* In case zlib doesn't work as I think.... */
  159. if (stream->avail_out >= stream->avail_in+16)
  160. break;
  161. case Z_BUF_ERROR:
  162. offset = stream->next_out - ((unsigned char*)*out);
  163. out_size *= 2;
  164. *out = tor_realloc(*out, out_size);
  165. stream->next_out = *out + offset;
  166. stream->avail_out = out_size - offset;
  167. break;
  168. default:
  169. log_fn(LOG_WARN, "Gzip decompression returned an error: %s",
  170. stream->msg ? stream->msg : "<no message>");
  171. goto err;
  172. }
  173. }
  174. done:
  175. *out_len = stream->total_out;
  176. if (inflateEnd(stream)!=Z_OK) {
  177. log_fn(LOG_WARN, "Error freeing gzip structures");
  178. goto err;
  179. }
  180. tor_free(stream);
  181. /* NUL-terminate output. */
  182. if (out_size == *out_len)
  183. *out = tor_realloc(*out, out_size + 1);
  184. (*out)[*out_len] = '\0';
  185. return 0;
  186. err:
  187. if (stream) {
  188. inflateEnd(stream);
  189. tor_free(stream);
  190. }
  191. if (*out) {
  192. tor_free(*out);
  193. }
  194. return -1;
  195. }