Line data Source code
1 : /* Copyright (c) 2004, Roger Dingledine.
2 : * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 : * Copyright (c) 2007-2021, The Tor Project, Inc. */
4 : /* See LICENSE for licensing information */
5 :
6 : /**
7 : * \file compress.c
8 : * \brief Common compression API implementation.
9 : *
10 : * This file provides a unified interface to all the compression libraries Tor
11 : * knows how to use.
12 : **/
13 :
14 : #include "orconfig.h"
15 :
16 : #include <stdlib.h>
17 : #include <stdio.h>
18 : #include <string.h>
19 : #include "lib/cc/torint.h"
20 :
21 : #ifdef HAVE_NETINET_IN_H
22 : #include <netinet/in.h>
23 : #endif
24 :
25 : #include "lib/log/log.h"
26 : #include "lib/log/util_bug.h"
27 : #include "lib/arch/bytes.h"
28 : #include "lib/ctime/di_ops.h"
29 : #include "lib/compress/compress.h"
30 : #include "lib/compress/compress_lzma.h"
31 : #include "lib/compress/compress_none.h"
32 : #include "lib/compress/compress_sys.h"
33 : #include "lib/compress/compress_zlib.h"
34 : #include "lib/compress/compress_zstd.h"
35 : #include "lib/intmath/cmp.h"
36 : #include "lib/malloc/malloc.h"
37 : #include "lib/subsys/subsys.h"
38 : #include "lib/thread/threads.h"
39 :
40 : /** Total number of bytes allocated for compression state overhead. */
41 : static atomic_counter_t total_compress_allocation;
42 :
43 : /** @{ */
44 : /* These macros define the maximum allowable compression factor. Anything of
45 : * size greater than CHECK_FOR_COMPRESSION_BOMB_AFTER is not allowed to
46 : * have an uncompression factor (uncompressed size:compressed size ratio) of
47 : * any greater than MAX_UNCOMPRESSION_FACTOR.
48 : *
49 : * Picking a value for MAX_UNCOMPRESSION_FACTOR is a trade-off: we want it to
50 : * be small to limit the attack multiplier, but we also want it to be large
51 : * enough so that no legitimate document --even ones we might invent in the
52 : * future -- ever compresses by a factor of greater than
53 : * MAX_UNCOMPRESSION_FACTOR. Within those parameters, there's a reasonably
54 : * large range of possible values. IMO, anything over 8 is probably safe; IMO
55 : * anything under 50 is probably sufficient.
56 : */
57 : #define MAX_UNCOMPRESSION_FACTOR 25
58 : #define CHECK_FOR_COMPRESSION_BOMB_AFTER (1024*64)
59 : /** @} */
60 :
61 : /** Return true if uncompressing an input of size <b>in_size</b> to an input of
62 : * size at least <b>size_out</b> looks like a compression bomb. */
63 392 : MOCK_IMPL(int,
64 : tor_compress_is_compression_bomb,(size_t size_in, size_t size_out))
65 : {
66 392 : if (size_in == 0 || size_out < CHECK_FOR_COMPRESSION_BOMB_AFTER)
67 : return 0;
68 :
69 19 : return (size_out / size_in > MAX_UNCOMPRESSION_FACTOR);
70 : }
71 :
72 : /** Guess the size that <b>in_len</b> will be after compression or
73 : * decompression. */
74 : static size_t
75 325 : guess_compress_size(int compress, compress_method_t method,
76 : compression_level_t compression_level,
77 : size_t in_len)
78 : {
79 : // ignore these for now.
80 325 : (void)compression_level;
81 325 : if (method == NO_METHOD) {
82 : /* Guess that we'll need an extra byte, to avoid a needless realloc
83 : * for nul-termination */
84 24 : return (in_len < SIZE_MAX) ? in_len + 1 : in_len;
85 : }
86 :
87 : /* Always guess a factor of 2. */
88 301 : if (compress) {
89 170 : in_len /= 2;
90 : } else {
91 131 : if (in_len < SIZE_T_CEILING/2)
92 131 : in_len *= 2;
93 : }
94 301 : return MAX(in_len, 1024);
95 : }
96 :
97 : /** Internal function to implement tor_compress/tor_uncompress, depending on
98 : * whether <b>compress</b> is set. All arguments are as for tor_compress or
99 : * tor_uncompress. */
100 : static int
101 325 : tor_compress_impl(int compress,
102 : char **out, size_t *out_len,
103 : const char *in, size_t in_len,
104 : compress_method_t method,
105 : compression_level_t compression_level,
106 : int complete_only,
107 : int protocol_warn_level)
108 : {
109 325 : tor_compress_state_t *stream;
110 325 : int rv;
111 :
112 325 : stream = tor_compress_new(compress, method, compression_level);
113 :
114 325 : if (stream == NULL) {
115 0 : log_warn(LD_GENERAL, "NULL stream while %scompressing",
116 : compress?"":"de");
117 0 : log_debug(LD_GENERAL, "method: %d level: %d at len: %lu",
118 : method, compression_level, (unsigned long)in_len);
119 0 : return -1;
120 : }
121 :
122 325 : size_t in_len_orig = in_len;
123 325 : size_t out_remaining, out_alloc;
124 325 : char *outptr;
125 :
126 325 : out_remaining = out_alloc =
127 325 : guess_compress_size(compress, method, compression_level, in_len);
128 325 : *out = outptr = tor_malloc(out_remaining);
129 :
130 325 : const int finish = complete_only || compress;
131 :
132 432 : while (1) {
133 432 : switch (tor_compress_process(stream,
134 : &outptr, &out_remaining,
135 : &in, &in_len, finish)) {
136 307 : case TOR_COMPRESS_DONE:
137 307 : if (in_len == 0 || compress) {
138 299 : goto done;
139 : } else {
140 : // More data is present, and we're decompressing. So we may need to
141 : // reinitialize the stream if we are handling multiple concatenated
142 : // inputs.
143 8 : tor_compress_free(stream);
144 8 : stream = tor_compress_new(compress, method, compression_level);
145 8 : if (stream == NULL) {
146 0 : log_warn(LD_GENERAL, "NULL stream while %scompressing",
147 : compress?"":"de");
148 0 : goto err;
149 : }
150 : }
151 : break;
152 25 : case TOR_COMPRESS_OK:
153 25 : if (compress || complete_only) {
154 4 : log_fn(protocol_warn_level, LD_PROTOCOL,
155 : "Unexpected %s while %scompressing",
156 : complete_only?"end of input":"result",
157 : compress?"":"de");
158 2 : log_debug(LD_GENERAL, "method: %d level: %d at len: %lu",
159 : method, compression_level, (unsigned long)in_len);
160 2 : goto err;
161 : } else {
162 23 : if (in_len == 0) {
163 9 : goto done;
164 : }
165 : }
166 : break;
167 88 : case TOR_COMPRESS_BUFFER_FULL: {
168 88 : if (!compress && outptr < *out+out_alloc) {
169 : // A buffer error in this case means that we have a problem
170 : // with our input.
171 3 : log_fn(protocol_warn_level, LD_PROTOCOL,
172 : "Possible truncated or corrupt compressed data");
173 3 : goto err;
174 : }
175 85 : if (out_alloc >= SIZE_T_CEILING / 2) {
176 0 : log_warn(LD_GENERAL, "While %scompressing data: ran out of space.",
177 : compress?"":"un");
178 0 : goto err;
179 : }
180 118 : if (!compress &&
181 33 : tor_compress_is_compression_bomb(in_len_orig, out_alloc)) {
182 : // This should already have been caught down in the backend logic.
183 : // LCOV_EXCL_START
184 : tor_assert_nonfatal_unreached();
185 : goto err;
186 : // LCOV_EXCL_STOP
187 : }
188 85 : const size_t offset = outptr - *out;
189 85 : out_alloc *= 2;
190 85 : *out = tor_realloc(*out, out_alloc);
191 85 : outptr = *out + offset;
192 85 : out_remaining = out_alloc - offset;
193 85 : break;
194 : }
195 12 : case TOR_COMPRESS_ERROR:
196 24 : log_fn(protocol_warn_level, LD_GENERAL,
197 : "Error while %scompressing data: bad input?",
198 : compress?"":"un");
199 12 : goto err; // bad data.
200 :
201 : // LCOV_EXCL_START
202 : default:
203 : tor_assert_nonfatal_unreached();
204 : goto err;
205 : // LCOV_EXCL_STOP
206 : }
207 : }
208 308 : done:
209 308 : *out_len = outptr - *out;
210 308 : if (compress && tor_compress_is_compression_bomb(*out_len, in_len_orig)) {
211 6 : log_warn(LD_BUG, "We compressed something and got an insanely high "
212 : "compression factor; other Tors would think this was a "
213 : "compression bomb.");
214 6 : goto err;
215 : }
216 302 : if (!compress) {
217 : // NUL-terminate our output.
218 134 : if (out_alloc == *out_len)
219 0 : *out = tor_realloc(*out, out_alloc + 1);
220 134 : (*out)[*out_len] = '\0';
221 : }
222 302 : rv = 0;
223 302 : goto out;
224 :
225 23 : err:
226 23 : tor_free(*out);
227 23 : *out_len = 0;
228 23 : rv = -1;
229 23 : goto out;
230 :
231 325 : out:
232 325 : tor_compress_free(stream);
233 325 : return rv;
234 : }
235 :
236 : /** Given <b>in_len</b> bytes at <b>in</b>, compress them into a newly
237 : * allocated buffer, using the method described in <b>method</b>. Store the
238 : * compressed string in *<b>out</b>, and its length in *<b>out_len</b>.
239 : * Return 0 on success, -1 on failure.
240 : */
241 : int
242 174 : tor_compress(char **out, size_t *out_len,
243 : const char *in, size_t in_len,
244 : compress_method_t method)
245 : {
246 174 : return tor_compress_impl(1, out, out_len, in, in_len, method,
247 : BEST_COMPRESSION,
248 : 1, LOG_WARN);
249 : }
250 :
251 : /** Given zero or more compressed strings of total length <b>in_len</b> bytes
252 : * at <b>in</b>, uncompress them into a newly allocated buffer, using the
253 : * method described in <b>method</b>. Store the uncompressed string in
254 : * *<b>out</b>, and its length in *<b>out_len</b>. Return 0 on success, -1 on
255 : * failure.
256 : *
257 : * If any bytes are written to <b>out</b>, an extra byte NUL is always
258 : * written at the end, but not counted in <b>out_len</b>. This is a
259 : * safety feature to ensure that the output can be treated as a
260 : * NUL-terminated string -- though of course, callers should check
261 : * out_len anyway.
262 : *
263 : * If <b>complete_only</b> is true, we consider a truncated input as a
264 : * failure; otherwise we decompress as much as we can. Warn about truncated
265 : * or corrupt inputs at <b>protocol_warn_level</b>.
266 : */
267 : int
268 151 : tor_uncompress(char **out, size_t *out_len,
269 : const char *in, size_t in_len,
270 : compress_method_t method,
271 : int complete_only,
272 : int protocol_warn_level)
273 : {
274 151 : return tor_compress_impl(0, out, out_len, in, in_len, method,
275 : BEST_COMPRESSION,
276 : complete_only, protocol_warn_level);
277 : }
278 :
279 : /** Try to tell whether the <b>in_len</b>-byte string in <b>in</b> is likely
280 : * to be compressed or not. If it is, return the likeliest compression method.
281 : * Otherwise, return UNKNOWN_METHOD.
282 : */
283 : compress_method_t
284 13 : detect_compression_method(const char *in, size_t in_len)
285 : {
286 13 : if (in_len > 2 && fast_memeq(in, "\x1f\x8b", 2)) {
287 : return GZIP_METHOD;
288 12 : } else if (in_len > 2 && (in[0] & 0x0f) == 8 &&
289 4 : (tor_ntohs(get_uint16(in)) % 31) == 0) {
290 : return ZLIB_METHOD;
291 10 : } else if (in_len > 2 &&
292 10 : fast_memeq(in, "\x5d\x00\x00", 3)) {
293 : return LZMA_METHOD;
294 9 : } else if (in_len > 3 &&
295 9 : fast_memeq(in, "\x28\xb5\x2f\xfd", 4)) {
296 : return ZSTD_METHOD;
297 : } else {
298 7 : return UNKNOWN_METHOD;
299 : }
300 : }
301 :
302 : /** Return 1 if a given <b>method</b> is supported; otherwise 0. */
303 : int
304 760 : tor_compress_supports_method(compress_method_t method)
305 : {
306 760 : switch (method) {
307 255 : case GZIP_METHOD:
308 : case ZLIB_METHOD:
309 255 : return tor_zlib_method_supported();
310 245 : case LZMA_METHOD:
311 245 : return tor_lzma_method_supported();
312 248 : case ZSTD_METHOD:
313 248 : return tor_zstd_method_supported();
314 : case NO_METHOD:
315 : return 1;
316 4 : case UNKNOWN_METHOD:
317 : default:
318 4 : return 0;
319 : }
320 : }
321 :
322 : /**
323 : * Return a bitmask of the supported compression types, where 1<<m is
324 : * set in the bitmask if and only if compression with method <b>m</b> is
325 : * supported.
326 : */
327 : unsigned
328 62 : tor_compress_get_supported_method_bitmask(void)
329 : {
330 62 : static unsigned supported = 0;
331 62 : if (supported == 0) {
332 : compress_method_t m;
333 28 : for (m = NO_METHOD; m <= UNKNOWN_METHOD; ++m) {
334 24 : if (tor_compress_supports_method(m)) {
335 20 : supported |= (1u << m);
336 : }
337 : }
338 : }
339 62 : return supported;
340 : }
341 :
342 : /** Table of compression method names. These should have an "x-" prefix,
343 : * if they are not listed in the IANA content coding registry. */
344 : static const struct {
345 : const char *name;
346 : compress_method_t method;
347 : } compression_method_names[] = {
348 : { "gzip", GZIP_METHOD },
349 : { "deflate", ZLIB_METHOD },
350 : // We call this "x-tor-lzma" rather than "x-lzma", because we impose a
351 : // lower maximum memory usage on the decoding side.
352 : { "x-tor-lzma", LZMA_METHOD },
353 : { "x-zstd" , ZSTD_METHOD },
354 : { "identity", NO_METHOD },
355 :
356 : /* Later entries in this table are not canonical; these are recognized but
357 : * not emitted. */
358 : { "x-gzip", GZIP_METHOD },
359 : };
360 :
361 : /** Return the canonical string representation of the compression method
362 : * <b>method</b>, or NULL if the method isn't recognized. */
363 : const char *
364 321 : compression_method_get_name(compress_method_t method)
365 : {
366 321 : unsigned i;
367 973 : for (i = 0; i < ARRAY_LENGTH(compression_method_names); ++i) {
368 973 : if (method == compression_method_names[i].method)
369 321 : return compression_method_names[i].name;
370 : }
371 : return NULL;
372 : }
373 :
374 : /** Table of compression human readable method names. */
375 : static const struct {
376 : compress_method_t method;
377 : const char *name;
378 : } compression_method_human_names[] = {
379 : { NO_METHOD, "uncompressed" },
380 : { GZIP_METHOD, "gzipped" },
381 : { ZLIB_METHOD, "deflated" },
382 : { LZMA_METHOD, "LZMA compressed" },
383 : { ZSTD_METHOD, "Zstandard compressed" },
384 : { UNKNOWN_METHOD, "unknown encoding" },
385 : };
386 :
387 : /** Return a human readable string representation of the compression method
388 : * <b>method</b>, or NULL if the method isn't recognized. */
389 : const char *
390 0 : compression_method_get_human_name(compress_method_t method)
391 : {
392 0 : unsigned i;
393 0 : for (i = 0; i < ARRAY_LENGTH(compression_method_human_names); ++i) {
394 0 : if (method == compression_method_human_names[i].method)
395 0 : return compression_method_human_names[i].name;
396 : }
397 : return NULL;
398 : }
399 :
400 : /** Return the compression method represented by the string <b>name</b>, or
401 : * UNKNOWN_METHOD if the string isn't recognized. */
402 : compress_method_t
403 109 : compression_method_get_by_name(const char *name)
404 : {
405 109 : unsigned i;
406 320 : for (i = 0; i < ARRAY_LENGTH(compression_method_names); ++i) {
407 311 : if (!strcmp(compression_method_names[i].name, name))
408 100 : return compression_method_names[i].method;
409 : }
410 : return UNKNOWN_METHOD;
411 : }
412 :
413 : /** Return a string representation of the version of the library providing the
414 : * compression method given in <b>method</b>. Returns NULL if <b>method</b> is
415 : * unknown or unsupported. */
416 : const char *
417 710 : tor_compress_version_str(compress_method_t method)
418 : {
419 710 : switch (method) {
420 237 : case GZIP_METHOD:
421 : case ZLIB_METHOD:
422 237 : return tor_zlib_get_version_str();
423 236 : case LZMA_METHOD:
424 236 : return tor_lzma_get_version_str();
425 237 : case ZSTD_METHOD:
426 237 : return tor_zstd_get_version_str();
427 : case NO_METHOD:
428 : case UNKNOWN_METHOD:
429 : default:
430 : return NULL;
431 : }
432 : }
433 :
434 : /** Return a string representation of the version of the library, found at
435 : * compile time, providing the compression method given in <b>method</b>.
436 : * Returns NULL if <b>method</b> is unknown or unsupported. */
437 : const char *
438 5 : tor_compress_header_version_str(compress_method_t method)
439 : {
440 5 : switch (method) {
441 2 : case GZIP_METHOD:
442 : case ZLIB_METHOD:
443 2 : return tor_zlib_get_header_version_str();
444 1 : case LZMA_METHOD:
445 1 : return tor_lzma_get_header_version_str();
446 2 : case ZSTD_METHOD:
447 2 : return tor_zstd_get_header_version_str();
448 : case NO_METHOD:
449 : case UNKNOWN_METHOD:
450 : default:
451 : return NULL;
452 : }
453 : }
454 :
455 : /** Return the approximate number of bytes allocated for all
456 : * supported compression schemas. */
457 : size_t
458 19 : tor_compress_get_total_allocation(void)
459 : {
460 19 : return atomic_counter_get(&total_compress_allocation) +
461 19 : tor_zlib_get_total_allocation() +
462 19 : tor_lzma_get_total_allocation() +
463 19 : tor_zstd_get_total_allocation();
464 : }
465 :
466 : /** Internal state for an incremental compression/decompression. The body of
467 : * this struct is not exposed. */
468 : struct tor_compress_state_t {
469 : compress_method_t method; /**< The compression method. */
470 :
471 : union {
472 : tor_zlib_compress_state_t *zlib_state;
473 : tor_lzma_compress_state_t *lzma_state;
474 : tor_zstd_compress_state_t *zstd_state;
475 : } u; /**< Compression backend state. */
476 : };
477 :
478 : /** Construct and return a tor_compress_state_t object using <b>method</b>. If
479 : * <b>compress</b>, it's for compression; otherwise it's for decompression. */
480 : tor_compress_state_t *
481 420 : tor_compress_new(int compress, compress_method_t method,
482 : compression_level_t compression_level)
483 : {
484 420 : tor_compress_state_t *state;
485 :
486 420 : state = tor_malloc_zero(sizeof(tor_compress_state_t));
487 420 : state->method = method;
488 :
489 420 : switch (method) {
490 187 : case GZIP_METHOD:
491 : case ZLIB_METHOD: {
492 187 : tor_zlib_compress_state_t *zlib_state =
493 187 : tor_zlib_compress_new(compress, method, compression_level);
494 :
495 187 : if (zlib_state == NULL)
496 0 : goto err;
497 :
498 187 : state->u.zlib_state = zlib_state;
499 187 : break;
500 : }
501 88 : case LZMA_METHOD: {
502 88 : tor_lzma_compress_state_t *lzma_state =
503 88 : tor_lzma_compress_new(compress, method, compression_level);
504 :
505 88 : if (lzma_state == NULL)
506 0 : goto err;
507 :
508 88 : state->u.lzma_state = lzma_state;
509 88 : break;
510 : }
511 105 : case ZSTD_METHOD: {
512 105 : tor_zstd_compress_state_t *zstd_state =
513 105 : tor_zstd_compress_new(compress, method, compression_level);
514 :
515 105 : if (zstd_state == NULL)
516 0 : goto err;
517 :
518 105 : state->u.zstd_state = zstd_state;
519 105 : break;
520 : }
521 : case NO_METHOD: {
522 : break;
523 : }
524 0 : case UNKNOWN_METHOD:
525 0 : goto err;
526 : }
527 :
528 420 : atomic_counter_add(&total_compress_allocation,
529 : sizeof(tor_compress_state_t));
530 420 : return state;
531 :
532 0 : err:
533 0 : tor_free(state);
534 0 : return NULL;
535 : }
536 :
537 : /** Compress/decompress some bytes using <b>state</b>. Read up to
538 : * *<b>in_len</b> bytes from *<b>in</b>, and write up to *<b>out_len</b> bytes
539 : * to *<b>out</b>, adjusting the values as we go. If <b>finish</b> is true,
540 : * we've reached the end of the input.
541 : *
542 : * Return TOR_COMPRESS_DONE if we've finished the entire
543 : * compression/decompression.
544 : * Return TOR_COMPRESS_OK if we're processed everything from the input.
545 : * Return TOR_COMPRESS_BUFFER_FULL if we're out of space on <b>out</b>.
546 : * Return TOR_COMPRESS_ERROR if the stream is corrupt.
547 : */
548 : tor_compress_output_t
549 776 : tor_compress_process(tor_compress_state_t *state,
550 : char **out, size_t *out_len,
551 : const char **in, size_t *in_len,
552 : int finish)
553 : {
554 776 : tor_assert(state != NULL);
555 776 : const size_t in_len_orig = *in_len;
556 776 : const size_t out_len_orig = *out_len;
557 776 : tor_compress_output_t rv;
558 :
559 776 : if (*out_len == 0 && (*in_len > 0 || finish)) {
560 : // If we still have input data, but no space for output data, we might as
561 : // well return early and let the caller do the reallocation of the out
562 : // variable.
563 : return TOR_COMPRESS_BUFFER_FULL;
564 : }
565 :
566 764 : switch (state->method) {
567 352 : case GZIP_METHOD:
568 : case ZLIB_METHOD:
569 352 : rv = tor_zlib_compress_process(state->u.zlib_state,
570 : out, out_len, in, in_len,
571 : finish);
572 352 : break;
573 153 : case LZMA_METHOD:
574 153 : rv = tor_lzma_compress_process(state->u.lzma_state,
575 : out, out_len, in, in_len,
576 : finish);
577 153 : break;
578 171 : case ZSTD_METHOD:
579 171 : rv = tor_zstd_compress_process(state->u.zstd_state,
580 : out, out_len, in, in_len,
581 : finish);
582 171 : break;
583 88 : case NO_METHOD:
584 88 : rv = tor_cnone_compress_process(out, out_len, in, in_len,
585 : finish);
586 88 : break;
587 0 : default:
588 : case UNKNOWN_METHOD:
589 0 : goto err;
590 : }
591 764 : if (BUG((rv == TOR_COMPRESS_OK) &&
592 : *in_len == in_len_orig &&
593 : *out_len == out_len_orig)) {
594 0 : log_warn(LD_GENERAL,
595 : "More info on the bug: method == %s, finish == %d, "
596 : " *in_len == in_len_orig == %lu, "
597 : "*out_len == out_len_orig == %lu",
598 : compression_method_get_human_name(state->method), finish,
599 : (unsigned long)in_len_orig, (unsigned long)out_len_orig);
600 0 : return TOR_COMPRESS_ERROR;
601 : }
602 :
603 : return rv;
604 0 : err:
605 0 : return TOR_COMPRESS_ERROR;
606 : }
607 :
608 : /** Deallocate <b>state</b>. */
609 : void
610 493 : tor_compress_free_(tor_compress_state_t *state)
611 : {
612 493 : if (state == NULL)
613 : return;
614 :
615 420 : switch (state->method) {
616 187 : case GZIP_METHOD:
617 : case ZLIB_METHOD:
618 187 : tor_zlib_compress_free(state->u.zlib_state);
619 187 : break;
620 88 : case LZMA_METHOD:
621 88 : tor_lzma_compress_free(state->u.lzma_state);
622 88 : break;
623 105 : case ZSTD_METHOD:
624 105 : tor_zstd_compress_free(state->u.zstd_state);
625 105 : break;
626 : case NO_METHOD:
627 : break;
628 : case UNKNOWN_METHOD:
629 : break;
630 : }
631 :
632 420 : atomic_counter_sub(&total_compress_allocation,
633 : sizeof(tor_compress_state_t));
634 420 : tor_free(state);
635 : }
636 :
637 : /** Return the approximate number of bytes allocated for <b>state</b>. */
638 : size_t
639 24 : tor_compress_state_size(const tor_compress_state_t *state)
640 : {
641 24 : tor_assert(state != NULL);
642 :
643 24 : size_t size = sizeof(tor_compress_state_t);
644 :
645 24 : switch (state->method) {
646 8 : case GZIP_METHOD:
647 : case ZLIB_METHOD:
648 8 : size += tor_zlib_compress_state_size(state->u.zlib_state);
649 8 : break;
650 4 : case LZMA_METHOD:
651 4 : size += tor_lzma_compress_state_size(state->u.lzma_state);
652 4 : break;
653 8 : case ZSTD_METHOD:
654 8 : size += tor_zstd_compress_state_size(state->u.zstd_state);
655 8 : break;
656 : case NO_METHOD:
657 : case UNKNOWN_METHOD:
658 : break;
659 : }
660 :
661 24 : return size;
662 : }
663 :
664 : /** Initialize all compression modules. */
665 : int
666 10862 : tor_compress_init(void)
667 : {
668 10862 : atomic_counter_init(&total_compress_allocation);
669 :
670 10862 : tor_zlib_init();
671 10862 : tor_lzma_init();
672 10862 : tor_zstd_init();
673 :
674 10862 : return 0;
675 : }
676 :
677 : /** Warn if we had any problems while setting up our compression libraries.
678 : *
679 : * (This isn't part of tor_compress_init, since the logs aren't set up yet.)
680 : */
681 : void
682 235 : tor_compress_log_init_warnings(void)
683 : {
684 : // XXXX can we move this into tor_compress_init() after all? log.c queues
685 : // XXXX log messages at startup.
686 235 : tor_zstd_warn_if_version_mismatched();
687 235 : }
688 :
689 : static int
690 5553 : subsys_compress_initialize(void)
691 : {
692 5553 : return tor_compress_init();
693 : }
694 :
695 : const subsys_fns_t sys_compress = {
696 : .name = "compress",
697 : SUBSYS_DECLARE_LOCATION(),
698 : .supported = true,
699 : .level = -55,
700 : .initialize = subsys_compress_initialize,
701 : };
|