torgzip.c
上传用户:awang829
上传日期:2019-07-14
资源大小:2356k
文件大小:12k
源码类别:

网络

开发平台:

Unix_Linux

  1. /* Copyright (c) 2004, Roger Dingledine.
  2.  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
  3.  * Copyright (c) 2007-2009, The Tor Project, Inc. */
  4. /* See LICENSE for licensing information */
  5. /**
  6.  * file torgzip.c
  7.  * brief A simple in-memory gzip implementation.
  8.  **/
  9. #include "orconfig.h"
  10. #include <stdlib.h>
  11. #include <stdio.h>
  12. #include <assert.h>
  13. #ifdef _MSC_VER
  14. #include "....contribzlibzlib.h"
  15. #else
  16. #include <zlib.h>
  17. #endif
  18. #include <string.h>
  19. #ifdef HAVE_NETINET_IN_H
  20. #include <netinet/in.h>
  21. #endif
  22. #include "util.h"
  23. #include "log.h"
  24. #include "torgzip.h"
  25. /** Set to 1 if zlib is a version that supports gzip; set to 0 if it doesn't;
  26.  * set to -1 if we haven't checked yet. */
  27. static int gzip_is_supported = -1;
  28. /** Return true iff we support gzip-based compression.  Otherwise, we need to
  29.  * use zlib. */
  30. int
  31. is_gzip_supported(void)
  32. {
  33.   if (gzip_is_supported >= 0)
  34.     return gzip_is_supported;
  35.   if (!strcmpstart(ZLIB_VERSION, "0.") ||
  36.       !strcmpstart(ZLIB_VERSION, "1.0") ||
  37.       !strcmpstart(ZLIB_VERSION, "1.1"))
  38.     gzip_is_supported = 0;
  39.   else
  40.     gzip_is_supported = 1;
  41.   return gzip_is_supported;
  42. }
  43. /** Return the 'bits' value to tell zlib to use <b>method</b>.*/
  44. static INLINE int
  45. method_bits(compress_method_t method)
  46. {
  47.   /* Bits+16 means "use gzip" in zlib >= 1.2 */
  48.   return method == GZIP_METHOD ? 15+16 : 15;
  49. }
  50. /** Given <b>in_len</b> bytes at <b>in</b>, compress them into a newly
  51.  * allocated buffer, using the method described in <b>method</b>.  Store the
  52.  * compressed string in *<b>out</b>, and its length in *<b>out_len</b>.
  53.  * Return 0 on success, -1 on failure.
  54.  */
  55. int
  56. tor_gzip_compress(char **out, size_t *out_len,
  57.                   const char *in, size_t in_len,
  58.                   compress_method_t method)
  59. {
  60.   struct z_stream_s *stream = NULL;
  61.   size_t out_size, old_size;
  62.   off_t offset;
  63.   tor_assert(out);
  64.   tor_assert(out_len);
  65.   tor_assert(in);
  66.   tor_assert(in_len < UINT_MAX);
  67.   *out = NULL;
  68.   if (method == GZIP_METHOD && !is_gzip_supported()) {
  69.     /* Old zlib version don't support gzip in deflateInit2 */
  70.     log_warn(LD_BUG, "Gzip not supported with zlib %s", ZLIB_VERSION);
  71.     goto err;
  72.   }
  73.   stream = tor_malloc_zero(sizeof(struct z_stream_s));
  74.   stream->zalloc = Z_NULL;
  75.   stream->zfree = Z_NULL;
  76.   stream->opaque = NULL;
  77.   stream->next_in = (unsigned char*) in;
  78.   stream->avail_in = (unsigned int)in_len;
  79.   if (deflateInit2(stream, Z_BEST_COMPRESSION, Z_DEFLATED,
  80.                    method_bits(method),
  81.                    8, Z_DEFAULT_STRATEGY) != Z_OK) {
  82.     log_warn(LD_GENERAL, "Error from deflateInit2: %s",
  83.              stream->msg?stream->msg:"<no message>");
  84.     goto err;
  85.   }
  86.   /* Guess 50% compression. */
  87.   out_size = in_len / 2;
  88.   if (out_size < 1024) out_size = 1024;
  89.   *out = tor_malloc(out_size);
  90.   stream->next_out = (unsigned char*)*out;
  91.   stream->avail_out = (unsigned int)out_size;
  92.   while (1) {
  93.     switch (deflate(stream, Z_FINISH))
  94.       {
  95.       case Z_STREAM_END:
  96.         goto done;
  97.       case Z_OK:
  98.         /* In case zlib doesn't work as I think .... */
  99.         if (stream->avail_out >= stream->avail_in+16)
  100.           break;
  101.       case Z_BUF_ERROR:
  102.         offset = stream->next_out - ((unsigned char*)*out);
  103.         old_size = out_size;
  104.         out_size *= 2;
  105.         if (out_size < old_size) {
  106.           log_warn(LD_GENERAL, "Size overflow in compression.");
  107.           goto err;
  108.         }
  109.         *out = tor_realloc(*out, out_size);
  110.         stream->next_out = (unsigned char*)(*out + offset);
  111.         if (out_size - offset > UINT_MAX) {
  112.           log_warn(LD_BUG,  "Ran over unsigned int limit of zlib while "
  113.                    "uncompressing.");
  114.           goto err;
  115.         }
  116.         stream->avail_out = (unsigned int)(out_size - offset);
  117.         break;
  118.       default:
  119.         log_warn(LD_GENERAL, "Gzip compression didn't finish: %s",
  120.                  stream->msg ? stream->msg : "<no message>");
  121.         goto err;
  122.       }
  123.   }
  124.  done:
  125.   *out_len = stream->total_out;
  126. #ifdef OPENBSD
  127.   /* "Hey Rocky!  Watch me change an unsigned field to a signed field in a
  128.    *    third-party API!"
  129.    * "Oh, that trick will just make people do unsafe casts to the unsigned
  130.    *    type in their cross-platform code!"
  131.    * "Don't be foolish.  I'm _sure_ they'll have the good sense to make sure
  132.    *    the newly unsigned field isn't negative." */
  133.   tor_assert(stream->total_out >= 0);
  134. #endif
  135.   if (((size_t)stream->total_out) > out_size + 4097) {
  136.     /* If we're wasting more than 4k, don't. */
  137.     *out = tor_realloc(*out, stream->total_out + 1);
  138.   }
  139.   if (deflateEnd(stream)!=Z_OK) {
  140.     log_warn(LD_BUG, "Error freeing gzip structures");
  141.     goto err;
  142.   }
  143.   tor_free(stream);
  144.   return 0;
  145.  err:
  146.   if (stream) {
  147.     deflateEnd(stream);
  148.     tor_free(stream);
  149.   }
  150.   if (*out) {
  151.     tor_free(*out);
  152.   }
  153.   return -1;
  154. }
  155. /** Given zero or more zlib-compressed or gzip-compressed strings of
  156.  * total length
  157.  * <b>in_len</b> bytes at <b>in</b>, uncompress them into a newly allocated
  158.  * buffer, using the method described in <b>method</b>.  Store the uncompressed
  159.  * string in *<b>out</b>, and its length in *<b>out_len</b>.  Return 0 on
  160.  * success, -1 on failure.
  161.  *
  162.  * If <b>complete_only</b> is true, we consider a truncated input as a
  163.  * failure; otherwise we decompress as much as we can.  Warn about truncated
  164.  * or corrupt inputs at <b>protocol_warn_level</b>.
  165.  */
  166. int
  167. tor_gzip_uncompress(char **out, size_t *out_len,
  168.                     const char *in, size_t in_len,
  169.                     compress_method_t method,
  170.                     int complete_only,
  171.                     int protocol_warn_level)
  172. {
  173.   struct z_stream_s *stream = NULL;
  174.   size_t out_size, old_size;
  175.   off_t offset;
  176.   int r;
  177.   tor_assert(out);
  178.   tor_assert(out_len);
  179.   tor_assert(in);
  180.   tor_assert(in_len < UINT_MAX);
  181.   if (method == GZIP_METHOD && !is_gzip_supported()) {
  182.     /* Old zlib version don't support gzip in inflateInit2 */
  183.     log_warn(LD_BUG, "Gzip not supported with zlib %s", ZLIB_VERSION);
  184.     return -1;
  185.   }
  186.   *out = NULL;
  187.   stream = tor_malloc_zero(sizeof(struct z_stream_s));
  188.   stream->zalloc = Z_NULL;
  189.   stream->zfree = Z_NULL;
  190.   stream->opaque = NULL;
  191.   stream->next_in = (unsigned char*) in;
  192.   stream->avail_in = (unsigned int)in_len;
  193.   if (inflateInit2(stream,
  194.                    method_bits(method)) != Z_OK) {
  195.     log_warn(LD_GENERAL, "Error from inflateInit2: %s",
  196.              stream->msg?stream->msg:"<no message>");
  197.     goto err;
  198.   }
  199.   out_size = in_len * 2;  /* guess 50% compression. */
  200.   if (out_size < 1024) out_size = 1024;
  201.   if (out_size > UINT_MAX)
  202.     goto err;
  203.   *out = tor_malloc(out_size);
  204.   stream->next_out = (unsigned char*)*out;
  205.   stream->avail_out = (unsigned int)out_size;
  206.   while (1) {
  207.     switch (inflate(stream, complete_only ? Z_FINISH : Z_SYNC_FLUSH))
  208.       {
  209.       case Z_STREAM_END:
  210.         if (stream->avail_in == 0)
  211.           goto done;
  212.         /* There may be more compressed data here. */
  213.         if ((r = inflateEnd(stream)) != Z_OK) {
  214.           log_warn(LD_BUG, "Error freeing gzip structures");
  215.           goto err;
  216.         }
  217.         if (inflateInit2(stream, method_bits(method)) != Z_OK) {
  218.           log_warn(LD_GENERAL, "Error from second inflateInit2: %s",
  219.                    stream->msg?stream->msg:"<no message>");
  220.           goto err;
  221.         }
  222.         break;
  223.       case Z_OK:
  224.         if (!complete_only && stream->avail_in == 0)
  225.           goto done;
  226.         /* In case zlib doesn't work as I think.... */
  227.         if (stream->avail_out >= stream->avail_in+16)
  228.           break;
  229.       case Z_BUF_ERROR:
  230.         if (stream->avail_out > 0) {
  231.           log_fn(protocol_warn_level, LD_PROTOCOL,
  232.                  "possible truncated or corrupt zlib data");
  233.           goto err;
  234.         }
  235.         offset = stream->next_out - (unsigned char*)*out;
  236.         old_size = out_size;
  237.         out_size *= 2;
  238.         if (out_size < old_size) {
  239.           log_warn(LD_GENERAL, "Size overflow in compression.");
  240.           goto err;
  241.         }
  242.         *out = tor_realloc(*out, out_size);
  243.         stream->next_out = (unsigned char*)(*out + offset);
  244.         if (out_size - offset > UINT_MAX) {
  245.           log_warn(LD_BUG,  "Ran over unsigned int limit of zlib while "
  246.                    "uncompressing.");
  247.           goto err;
  248.         }
  249.         stream->avail_out = (unsigned int)(out_size - offset);
  250.         break;
  251.       default:
  252.         log_warn(LD_GENERAL, "Gzip decompression returned an error: %s",
  253.                  stream->msg ? stream->msg : "<no message>");
  254.         goto err;
  255.       }
  256.   }
  257.  done:
  258.   *out_len = stream->next_out - (unsigned char*)*out;
  259.   r = inflateEnd(stream);
  260.   tor_free(stream);
  261.   if (r != Z_OK) {
  262.     log_warn(LD_BUG, "Error freeing gzip structures");
  263.     goto err;
  264.   }
  265.   /* NUL-terminate output. */
  266.   if (out_size == *out_len)
  267.     *out = tor_realloc(*out, out_size + 1);
  268.   (*out)[*out_len] = '';
  269.   return 0;
  270.  err:
  271.   if (stream) {
  272.     inflateEnd(stream);
  273.     tor_free(stream);
  274.   }
  275.   if (*out) {
  276.     tor_free(*out);
  277.   }
  278.   return -1;
  279. }
  280. /** Try to tell whether the <b>in_len</b>-byte string in <b>in</b> is likely
  281.  * to be compressed or not.  If it is, return the likeliest compression method.
  282.  * Otherwise, return UNKNOWN_METHOD.
  283.  */
  284. compress_method_t
  285. detect_compression_method(const char *in, size_t in_len)
  286. {
  287.   if (in_len > 2 && !memcmp(in, "x1fx8b", 2)) {
  288.     return GZIP_METHOD;
  289.   } else if (in_len > 2 && (in[0] & 0x0f) == 8 &&
  290.              (ntohs(get_uint16(in)) % 31) == 0) {
  291.     return ZLIB_METHOD;
  292.   } else {
  293.     return UNKNOWN_METHOD;
  294.   }
  295. }
  296. /** Internal state for an incremental zlib compression/decompression.  The
  297.  * body of this struct is not exposed. */
  298. struct tor_zlib_state_t {
  299.   struct z_stream_s stream;
  300.   int compress;
  301. };
  302. /** Construct and return a tor_zlib_state_t object using <b>method</b>.  If
  303.  * <b>compress</b>, it's for compression; otherwise it's for
  304.  * decompression. */
  305. tor_zlib_state_t *
  306. tor_zlib_new(int compress, compress_method_t method)
  307. {
  308.   tor_zlib_state_t *out;
  309.   if (method == GZIP_METHOD && !is_gzip_supported()) {
  310.     /* Old zlib version don't support gzip in inflateInit2 */
  311.     log_warn(LD_BUG, "Gzip not supported with zlib %s", ZLIB_VERSION);
  312.     return NULL;
  313.  }
  314.  out = tor_malloc_zero(sizeof(tor_zlib_state_t));
  315.  out->stream.zalloc = Z_NULL;
  316.  out->stream.zfree = Z_NULL;
  317.  out->stream.opaque = NULL;
  318.  out->compress = compress;
  319.  if (compress) {
  320.    if (deflateInit2(&out->stream, Z_BEST_COMPRESSION, Z_DEFLATED,
  321.                     method_bits(method), 8, Z_DEFAULT_STRATEGY) != Z_OK)
  322.      goto err;
  323.  } else {
  324.    if (inflateInit2(&out->stream, method_bits(method)) != Z_OK)
  325.      goto err;
  326.  }
  327.  return out;
  328.  err:
  329.  tor_free(out);
  330.  return NULL;
  331. }
  332. /** Compress/decompress some bytes using <b>state</b>.  Read up to
  333.  * *<b>in_len</b> bytes from *<b>in</b>, and write up to *<b>out_len</b> bytes
  334.  * to *<b>out</b>, adjusting the values as we go.  If <b>finish</b> is true,
  335.  * we've reached the end of the input.
  336.  *
  337.  * Return TOR_ZLIB_DONE if we've finished the entire compression/decompression.
  338.  * Return TOR_ZLIB_OK if we're processed everything from the input.
  339.  * Return TOR_ZLIB_BUF_FULL if we're out of space on <b>out</b>.
  340.  * Return TOR_ZLIB_ERR if the stream is corrupt.
  341.  */
  342. tor_zlib_output_t
  343. tor_zlib_process(tor_zlib_state_t *state,
  344.                  char **out, size_t *out_len,
  345.                  const char **in, size_t *in_len,
  346.                  int finish)
  347. {
  348.   int err;
  349.   tor_assert(*in_len <= UINT_MAX);
  350.   tor_assert(*out_len <= UINT_MAX);
  351.   state->stream.next_in = (unsigned char*) *in;
  352.   state->stream.avail_in = (unsigned int)*in_len;
  353.   state->stream.next_out = (unsigned char*) *out;
  354.   state->stream.avail_out = (unsigned int)*out_len;
  355.   if (state->compress) {
  356.     err = deflate(&state->stream, finish ? Z_FINISH : Z_SYNC_FLUSH);
  357.   } else {
  358.     err = inflate(&state->stream, finish ? Z_FINISH : Z_SYNC_FLUSH);
  359.   }
  360.   *out = (char*) state->stream.next_out;
  361.   *out_len = state->stream.avail_out;
  362.   *in = (const char *) state->stream.next_in;
  363.   *in_len = state->stream.avail_in;
  364.   switch (err)
  365.     {
  366.     case Z_STREAM_END:
  367.       return TOR_ZLIB_DONE;
  368.     case Z_BUF_ERROR:
  369.       if (state->stream.avail_in == 0)
  370.         return TOR_ZLIB_OK;
  371.       return TOR_ZLIB_BUF_FULL;
  372.     case Z_OK:
  373.       if (state->stream.avail_out == 0 || finish)
  374.         return TOR_ZLIB_BUF_FULL;
  375.       return TOR_ZLIB_OK;
  376.     default:
  377.       log_warn(LD_GENERAL, "Gzip returned an error: %s",
  378.                state->stream.msg ? state->stream.msg : "<no message>");
  379.       return TOR_ZLIB_ERR;
  380.     }
  381. }
  382. /** Deallocate <b>state</b>. */
  383. void
  384. tor_zlib_free(tor_zlib_state_t *state)
  385. {
  386.   tor_assert(state);
  387.   if (state->compress)
  388.     deflateEnd(&state->stream);
  389.   else
  390.     inflateEnd(&state->stream);
  391.   tor_free(state);
  392. }