diff options
Diffstat (limited to 'lib/compress.c')
-rw-r--r-- | lib/compress.c | 439 |
1 files changed, 439 insertions, 0 deletions
diff --git a/lib/compress.c b/lib/compress.c new file mode 100644 index 0000000000..2e7197fb63 --- /dev/null +++ b/lib/compress.c @@ -0,0 +1,439 @@ +/* + * Copyright (C) 2000-2012 Free Software Foundation, 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 <http://www.gnu.org/licenses/> + * + */ + +/* This file contains the functions which convert the TLS plaintext + * packet to TLS compressed packet. + */ + +#include "gnutls_int.h" +#include "compress.h" +#include "errors.h" +#include "constate.h" +#include <algorithms.h> +#include <gnutls/gnutls.h> + +/* Compression Section */ +#define GNUTLS_COMPRESSION_ENTRY(name, id, wb, ml, cl) \ + { #name, name, id, wb, ml, cl} + + +#define MAX_COMP_METHODS 5 +const int _gnutls_comp_algorithms_size = MAX_COMP_METHODS; + +gnutls_compression_entry _gnutls_compression_algorithms[MAX_COMP_METHODS] = { + GNUTLS_COMPRESSION_ENTRY(GNUTLS_COMP_NULL, 0x00, 0, 0, 0), +#ifdef HAVE_LIBZ + /* draft-ietf-tls-compression-02 */ + GNUTLS_COMPRESSION_ENTRY(GNUTLS_COMP_DEFLATE, 0x01, 15, 8, 3), +#endif + {0, 0, 0, 0, 0, 0} +}; + +static const gnutls_compression_method_t supported_compressions[] = { +#ifdef HAVE_LIBZ + GNUTLS_COMP_DEFLATE, +#endif + GNUTLS_COMP_NULL, + 0 +}; + +#define GNUTLS_COMPRESSION_LOOP(b) \ + const gnutls_compression_entry *p; \ + for(p = _gnutls_compression_algorithms; p->name != NULL; p++) { b ; } +#define GNUTLS_COMPRESSION_ALG_LOOP(a) \ + GNUTLS_COMPRESSION_LOOP( if(p->id == algorithm) { a; break; } ) +#define GNUTLS_COMPRESSION_ALG_LOOP_NUM(a) \ + GNUTLS_COMPRESSION_LOOP( if(p->num == num) { a; break; } ) + +/* Compression Functions */ + +/** + * 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 char *ret = NULL; + + /* avoid prefix */ + GNUTLS_COMPRESSION_ALG_LOOP(ret = + p->name + sizeof("GNUTLS_COMP_") - 1); + + return ret; +} + +/** + * 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) +{ + gnutls_compression_method_t ret = GNUTLS_COMP_UNKNOWN; + + GNUTLS_COMPRESSION_LOOP(if + (strcasecmp + (p->name + sizeof("GNUTLS_COMP_") - 1, + name) == 0) ret = p->id); + + return ret; +} + +/** + * 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 supported_compressions; +} + +/* return the tls number of the specified algorithm */ +int _gnutls_compression_get_num(gnutls_compression_method_t algorithm) +{ + int ret = -1; + + /* avoid prefix */ + GNUTLS_COMPRESSION_ALG_LOOP(ret = p->num); + + return ret; +} + +#ifdef HAVE_LIBZ + +static int get_wbits(gnutls_compression_method_t algorithm) +{ + int ret = -1; + /* avoid prefix */ + GNUTLS_COMPRESSION_ALG_LOOP(ret = p->window_bits); + return ret; +} + +static int get_mem_level(gnutls_compression_method_t algorithm) +{ + int ret = -1; + /* avoid prefix */ + GNUTLS_COMPRESSION_ALG_LOOP(ret = p->mem_level); + return ret; +} + +static int get_comp_level(gnutls_compression_method_t algorithm) +{ + int ret = -1; + /* avoid prefix */ + GNUTLS_COMPRESSION_ALG_LOOP(ret = p->comp_level); + return ret; +} + +#endif + +/* returns the gnutls internal ID of the TLS compression + * method num + */ +gnutls_compression_method_t _gnutls_compression_get_id(int num) +{ + gnutls_compression_method_t ret = -1; + + /* avoid prefix */ + GNUTLS_COMPRESSION_ALG_LOOP_NUM(ret = p->id); + + return ret; +} + +int _gnutls_compression_is_ok(gnutls_compression_method_t algorithm) +{ + ssize_t ret = -1; + GNUTLS_COMPRESSION_ALG_LOOP(ret = p->id); + if (ret >= 0) + ret = 0; + else + ret = 1; + return ret; +} + + + +/* For compression */ + +#define MIN_PRIVATE_COMP_ALGO 0xEF + +/* returns the TLS numbers of the compression methods we support + */ +#define SUPPORTED_COMPRESSION_METHODS session->internals.priorities.compression.algorithms +int +_gnutls_supported_compression_methods(gnutls_session_t session, + uint8_t * comp, size_t comp_size) +{ + unsigned int i, j; + int tmp; + + if (comp_size < SUPPORTED_COMPRESSION_METHODS) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + for (i = j = 0; i < SUPPORTED_COMPRESSION_METHODS; i++) { + if (IS_DTLS(session) && session->internals.priorities.compression.priority[i] != GNUTLS_COMP_NULL) { + gnutls_assert(); + continue; + } + + tmp = + _gnutls_compression_get_num(session-> + internals.priorities. + compression.priority[i]); + + /* remove private compression algorithms, if requested. + */ + if (tmp == -1 || (tmp >= MIN_PRIVATE_COMP_ALGO && + session->internals.enable_private == 0)) + { + gnutls_assert(); + continue; + } + + comp[j] = (uint8_t) tmp; + j++; + } + + if (j == 0) { + gnutls_assert(); + return GNUTLS_E_NO_COMPRESSION_ALGORITHMS; + } + return j; +} + + +/* The flag d is the direction (compress, decompress). Non zero is + * decompress. + */ +int _gnutls_comp_init(comp_hd_st * handle, + gnutls_compression_method_t method, int d) +{ + handle->algo = method; + handle->handle = NULL; + + switch (method) { + case GNUTLS_COMP_DEFLATE: +#ifdef HAVE_LIBZ + { + int window_bits, mem_level; + int comp_level; + z_stream *zhandle; + int err; + + window_bits = get_wbits(method); + mem_level = get_mem_level(method); + comp_level = get_comp_level(method); + + handle->handle = gnutls_malloc(sizeof(z_stream)); + if (handle->handle == NULL) + return + gnutls_assert_val + (GNUTLS_E_MEMORY_ERROR); + + zhandle = handle->handle; + + zhandle->zalloc = (alloc_func) 0; + zhandle->zfree = (free_func) 0; + zhandle->opaque = (voidpf) 0; + + if (d) + err = inflateInit2(zhandle, window_bits); + else { + err = deflateInit2(zhandle, + comp_level, Z_DEFLATED, + window_bits, mem_level, + Z_DEFAULT_STRATEGY); + } + if (err != Z_OK) { + gnutls_assert(); + gnutls_free(handle->handle); + return GNUTLS_E_COMPRESSION_FAILED; + } + } + break; +#endif + case GNUTLS_COMP_NULL: + case GNUTLS_COMP_UNKNOWN: + break; + default: + return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM; + } + + return 0; +} + +/* The flag d is the direction (compress, decompress). Non zero is + * decompress. + */ +void _gnutls_comp_deinit(comp_hd_st * handle, int d) +{ + if (handle != NULL) { + switch (handle->algo) { +#ifdef HAVE_LIBZ + case GNUTLS_COMP_DEFLATE: + { + if (d) + inflateEnd(handle->handle); + else + deflateEnd(handle->handle); + break; + } +#endif + default: + break; + } + gnutls_free(handle->handle); + handle->handle = NULL; + } +} + +/* These functions are memory consuming + */ + +int +_gnutls_compress(comp_hd_st * handle, const uint8_t * plain, + size_t plain_size, uint8_t * compressed, + size_t max_comp_size, unsigned int stateless) +{ + int compressed_size = GNUTLS_E_COMPRESSION_FAILED; + + /* NULL compression is not handled here + */ + if (handle == NULL) { + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; + } + + switch (handle->algo) { +#ifdef HAVE_LIBZ + case GNUTLS_COMP_DEFLATE: + { + z_stream *zhandle; + int err; + int type; + + if (stateless) { + type = Z_FULL_FLUSH; + } else + type = Z_SYNC_FLUSH; + + zhandle = handle->handle; + + zhandle->next_in = (Bytef *) plain; + zhandle->avail_in = plain_size; + zhandle->next_out = (Bytef *) compressed; + zhandle->avail_out = max_comp_size; + + err = deflate(zhandle, type); + if (err != Z_OK || zhandle->avail_in != 0) + return + gnutls_assert_val + (GNUTLS_E_COMPRESSION_FAILED); + + + compressed_size = + max_comp_size - zhandle->avail_out; + break; + } +#endif + default: + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; + } /* switch */ + +#ifdef COMPRESSION_DEBUG + _gnutls_debug_log("Compression ratio: %f\n", + (float) ((float) compressed_size / + (float) plain_size)); +#endif + + return compressed_size; +} + + + +int +_gnutls_decompress(comp_hd_st * handle, uint8_t * compressed, + size_t compressed_size, uint8_t * plain, + size_t max_plain_size) +{ + int plain_size = GNUTLS_E_DECOMPRESSION_FAILED; + + if (compressed_size > max_plain_size + EXTRA_COMP_SIZE) { + gnutls_assert(); + return GNUTLS_E_DECOMPRESSION_FAILED; + } + + /* NULL compression is not handled here + */ + + if (handle == NULL) { + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; + } + + switch (handle->algo) { +#ifdef HAVE_LIBZ + case GNUTLS_COMP_DEFLATE: + { + z_stream *zhandle; + int err; + + zhandle = handle->handle; + + zhandle->next_in = (Bytef *) compressed; + zhandle->avail_in = compressed_size; + + zhandle->next_out = (Bytef *) plain; + zhandle->avail_out = max_plain_size; + err = inflate(zhandle, Z_SYNC_FLUSH); + + if (err != Z_OK) + return + gnutls_assert_val + (GNUTLS_E_DECOMPRESSION_FAILED); + + plain_size = max_plain_size - zhandle->avail_out; + break; + } +#endif + default: + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; + } /* switch */ + + return plain_size; +} |