diff options
Diffstat (limited to 'lib/compress.c')
-rw-r--r-- | lib/compress.c | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/lib/compress.c b/lib/compress.c new file mode 100644 index 0000000..dbcf4fa --- /dev/null +++ b/lib/compress.c @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2017-2022 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "compress.h" + +#ifdef HAVE_LIBZ +#include <zlib.h> +#endif + +#ifdef HAVE_LIBBROTLI +#include <brotli/decode.h> +#include <brotli/encode.h> +#endif + +#ifdef HAVE_LIBZSTD +#include <zstd.h> +#endif + +typedef struct { + gnutls_compression_method_t id; + const char *name; +} comp_entry; + +static const comp_entry comp_algs[] = { + { GNUTLS_COMP_NULL, "NULL" }, +#ifdef HAVE_LIBZ + { GNUTLS_COMP_ZLIB, "ZLIB" }, +#endif +#ifdef HAVE_LIBBROTLI + { GNUTLS_COMP_BROTLI, "BROTLI" }, +#endif +#ifdef HAVE_LIBZSTD + { GNUTLS_COMP_ZSTD, "ZSTD" }, +#endif + { GNUTLS_COMP_UNKNOWN, NULL } +}; + +static const gnutls_compression_method_t alg_list[] = { + GNUTLS_COMP_NULL, +#ifdef HAVE_LIBZ + GNUTLS_COMP_ZLIB, +#endif +#ifdef HAVE_LIBBROTLI + GNUTLS_COMP_BROTLI, +#endif +#ifdef HAVE_LIBZSTD + GNUTLS_COMP_ZSTD, +#endif + 0 +}; + +/** + * gnutls_compression_get_name: + * @algorithm: is a Compression algorithm + * + * Convert a #gnutls_compression_method_t value to a string. + * + * Returns: a pointer to a string that contains the name of the + * specified compression algorithm, or %NULL. + **/ +const char * +gnutls_compression_get_name(gnutls_compression_method_t algorithm) +{ + const comp_entry *p; + + for (p = comp_algs; p->name; ++p) + if (p->id == algorithm) + return p->name; + + return NULL; +} + +/** + * gnutls_compression_get_id: + * @name: is a compression method name + * + * The names are compared in a case insensitive way. + * + * Returns: an id of the specified in a string compression method, or + * %GNUTLS_COMP_UNKNOWN on error. + **/ +gnutls_compression_method_t +gnutls_compression_get_id(const char *name) +{ + const comp_entry *p; + + for (p = comp_algs; p->name; ++p) + if (!strcasecmp(p->name, name)) + return p->id; + + return GNUTLS_COMP_UNKNOWN; +} + +/** + * gnutls_compression_list: + * + * Get a list of compression methods. + * + * Returns: a zero-terminated list of #gnutls_compression_method_t + * integers indicating the available compression methods. + **/ +const gnutls_compression_method_t * +gnutls_compression_list(void) +{ + return alg_list; +} + + +/*************************/ +/* Compression functions */ +/*************************/ + + +size_t +_gnutls_compress_bound(gnutls_compression_method_t alg, size_t src_len) +{ + switch (alg) { +#ifdef HAVE_LIBZ + case GNUTLS_COMP_ZLIB: + return compressBound(src_len); +#endif +#ifdef HAVE_LIBBROTLI + case GNUTLS_COMP_BROTLI: + return BrotliEncoderMaxCompressedSize(src_len); +#endif +#ifdef HAVE_LIBZSTD + case GNUTLS_COMP_ZSTD: + return ZSTD_compressBound(src_len); +#endif + default: + return 0; + } + return 0; +} + +int +_gnutls_compress(gnutls_compression_method_t alg, + uint8_t * dst, size_t dst_len, + const uint8_t * src, size_t src_len) +{ + int ret = GNUTLS_E_COMPRESSION_FAILED; + + switch (alg) { +#ifdef HAVE_LIBZ + case GNUTLS_COMP_ZLIB: + { + int err; + uLongf comp_len = dst_len; + + err = compress(dst, &comp_len, src, src_len); + if (err != Z_OK) + return gnutls_assert_val(GNUTLS_E_COMPRESSION_FAILED); + ret = comp_len; + } + break; +#endif +#ifdef HAVE_LIBBROTLI + case GNUTLS_COMP_BROTLI: + { + BROTLI_BOOL err; + size_t comp_len = dst_len; + + err = BrotliEncoderCompress(BROTLI_DEFAULT_QUALITY, + BROTLI_DEFAULT_WINDOW, + BROTLI_DEFAULT_MODE, + src_len, src, &comp_len, dst); + if (!err) + return gnutls_assert_val(GNUTLS_E_COMPRESSION_FAILED); + ret = comp_len; + } + break; +#endif +#ifdef HAVE_LIBZSTD + case GNUTLS_COMP_ZSTD: + { + size_t comp_len; + + comp_len = ZSTD_compress(dst, dst_len, src, src_len, ZSTD_CLEVEL_DEFAULT); + if (ZSTD_isError(comp_len)) + return gnutls_assert_val(GNUTLS_E_COMPRESSION_FAILED); + ret = comp_len; + } + break; +#endif + default: + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } + +#ifdef COMPRESSION_DEBUG + _gnutls_debug_log("Compression ratio: %f\n", (float)((float)ret / (float)src_len)); +#endif + + return ret; +} + +int +_gnutls_decompress(gnutls_compression_method_t alg, + uint8_t * dst, size_t dst_len, + const uint8_t * src, size_t src_len) +{ + int ret = GNUTLS_E_DECOMPRESSION_FAILED; + + switch (alg) { +#ifdef HAVE_LIBZ + case GNUTLS_COMP_ZLIB: + { + int err; + uLongf plain_len = dst_len; + + err = uncompress(dst, &plain_len, src, src_len); + if (err != Z_OK) + return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED); + ret = plain_len; + } + break; +#endif +#ifdef HAVE_LIBBROTLI + case GNUTLS_COMP_BROTLI: + { + BrotliDecoderResult err; + size_t plain_len = dst_len; + + err = BrotliDecoderDecompress(src_len, src, &plain_len, dst); + if (err != BROTLI_DECODER_RESULT_SUCCESS) + return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED); + ret = plain_len; + } + break; +#endif +#ifdef HAVE_LIBZSTD + case GNUTLS_COMP_ZSTD: + { + size_t plain_len; + + plain_len = ZSTD_decompress(dst, dst_len, src, src_len); + if (ZSTD_isError(plain_len)) + return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED); + ret = plain_len; + } + break; +#endif + default: + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } + + return ret; +} |