updated libtasn1
[gnutls.git] / lib / gnutls_compress.c
blob7cd6de8527df107575e41db35e28287d137e83b6
1 /*
2 * Copyright (C) 2000, 2004, 2005, 2007, 2008, 2010 Free Software
3 * Foundation, Inc.
5 * Author: Nikos Mavrogiannopoulos
7 * This file is part of GnuTLS.
9 * The GnuTLS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1 of
12 * the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22 * USA
26 /* This file contains the functions which convert the TLS plaintext
27 * packet to TLS compressed packet.
30 #include "gnutls_int.h"
31 #include "gnutls_compress.h"
32 #include "gnutls_errors.h"
33 #include "gnutls_constate.h"
34 #include <gnutls_algorithms.h>
35 #include <gnutls/gnutls.h>
37 /* These functions allocate the return value internally
39 int
40 _gnutls_m_plaintext2compressed (gnutls_session_t session,
41 gnutls_datum_t * compressed,
42 const gnutls_datum_t * plaintext,
43 const record_parameters_st * params)
45 int size;
46 opaque *data;
48 size =
49 _gnutls_compress (params->write.compression_state,
50 plaintext->data, plaintext->size, &data,
51 MAX_RECORD_SEND_SIZE + EXTRA_COMP_SIZE);
52 if (size < 0)
54 gnutls_assert ();
55 return GNUTLS_E_COMPRESSION_FAILED;
57 compressed->data = data;
58 compressed->size = size;
60 return 0;
63 int
64 _gnutls_m_compressed2plaintext (gnutls_session_t session,
65 gnutls_datum_t * plain,
66 const gnutls_datum_t * compressed,
67 const record_parameters_st * params)
69 int size;
70 opaque *data;
72 size =
73 _gnutls_decompress (params->read.compression_state,
74 compressed->data, compressed->size, &data,
75 MAX_RECORD_RECV_SIZE);
76 if (size < 0)
78 gnutls_assert ();
79 return GNUTLS_E_DECOMPRESSION_FAILED;
81 plain->data = data;
82 plain->size = size;
84 return 0;
88 /* Compression Section */
89 #define GNUTLS_COMPRESSION_ENTRY(name, id, wb, ml, cl) \
90 { #name, name, id, wb, ml, cl}
93 #define MAX_COMP_METHODS 5
94 const int _gnutls_comp_algorithms_size = MAX_COMP_METHODS;
96 gnutls_compression_entry _gnutls_compression_algorithms[MAX_COMP_METHODS] = {
97 GNUTLS_COMPRESSION_ENTRY (GNUTLS_COMP_NULL, 0x00, 0, 0, 0),
98 #ifdef HAVE_LIBZ
99 /* draft-ietf-tls-compression-02 */
100 GNUTLS_COMPRESSION_ENTRY (GNUTLS_COMP_DEFLATE, 0x01, 15, 8, 3),
101 #endif
102 {0, 0, 0, 0, 0, 0}
105 static const gnutls_compression_method_t supported_compressions[] = {
106 #ifdef USE_LZO
107 GNUTLS_COMP_LZO,
108 #endif
109 #ifdef HAVE_LIBZ
110 GNUTLS_COMP_DEFLATE,
111 #endif
112 GNUTLS_COMP_NULL,
116 #define GNUTLS_COMPRESSION_LOOP(b) \
117 const gnutls_compression_entry *p; \
118 for(p = _gnutls_compression_algorithms; p->name != NULL; p++) { b ; }
119 #define GNUTLS_COMPRESSION_ALG_LOOP(a) \
120 GNUTLS_COMPRESSION_LOOP( if(p->id == algorithm) { a; break; } )
121 #define GNUTLS_COMPRESSION_ALG_LOOP_NUM(a) \
122 GNUTLS_COMPRESSION_LOOP( if(p->num == num) { a; break; } )
124 /* Compression Functions */
127 * gnutls_compression_get_name:
128 * @algorithm: is a Compression algorithm
130 * Convert a #gnutls_compression_method_t value to a string.
132 * Returns: a pointer to a string that contains the name of the
133 * specified compression algorithm, or %NULL.
135 const char *
136 gnutls_compression_get_name (gnutls_compression_method_t algorithm)
138 const char *ret = NULL;
140 /* avoid prefix */
141 GNUTLS_COMPRESSION_ALG_LOOP (ret = p->name + sizeof ("GNUTLS_COMP_") - 1);
143 return ret;
147 * gnutls_compression_get_id:
148 * @name: is a compression method name
150 * The names are compared in a case insensitive way.
152 * Returns: an id of the specified in a string compression method, or
153 * %GNUTLS_COMP_UNKNOWN on error.
155 gnutls_compression_method_t
156 gnutls_compression_get_id (const char *name)
158 gnutls_compression_method_t ret = GNUTLS_COMP_UNKNOWN;
160 GNUTLS_COMPRESSION_LOOP (if
161 (strcasecmp
162 (p->name + sizeof ("GNUTLS_COMP_") - 1,
163 name) == 0) ret = p->id);
165 return ret;
169 * gnutls_compression_list:
171 * Get a list of compression methods. Note that to be able to use LZO
172 * compression, you must link to libgnutls-extra and call
173 * gnutls_global_init_extra().
175 * Returns: a zero-terminated list of #gnutls_compression_method_t
176 * integers indicating the available compression methods.
178 const gnutls_compression_method_t *
179 gnutls_compression_list (void)
181 return supported_compressions;
184 /* return the tls number of the specified algorithm */
186 _gnutls_compression_get_num (gnutls_compression_method_t algorithm)
188 int ret = -1;
190 /* avoid prefix */
191 GNUTLS_COMPRESSION_ALG_LOOP (ret = p->num);
193 return ret;
196 #ifdef HAVE_LIBZ
198 static int
199 get_wbits (gnutls_compression_method_t algorithm)
201 int ret = -1;
202 /* avoid prefix */
203 GNUTLS_COMPRESSION_ALG_LOOP (ret = p->window_bits);
204 return ret;
207 static int
208 get_mem_level (gnutls_compression_method_t algorithm)
210 int ret = -1;
211 /* avoid prefix */
212 GNUTLS_COMPRESSION_ALG_LOOP (ret = p->mem_level);
213 return ret;
216 static int
217 get_comp_level (gnutls_compression_method_t algorithm)
219 int ret = -1;
220 /* avoid prefix */
221 GNUTLS_COMPRESSION_ALG_LOOP (ret = p->comp_level);
222 return ret;
225 #endif
227 /* returns the gnutls internal ID of the TLS compression
228 * method num
230 gnutls_compression_method_t
231 _gnutls_compression_get_id (int num)
233 gnutls_compression_method_t ret = -1;
235 /* avoid prefix */
236 GNUTLS_COMPRESSION_ALG_LOOP_NUM (ret = p->id);
238 return ret;
242 _gnutls_compression_is_ok (gnutls_compression_method_t algorithm)
244 ssize_t ret = -1;
245 GNUTLS_COMPRESSION_ALG_LOOP (ret = p->id);
246 if (ret >= 0)
247 ret = 0;
248 else
249 ret = 1;
250 return ret;
255 /* For compression */
257 #define MIN_PRIVATE_COMP_ALGO 0xEF
259 /* returns the TLS numbers of the compression methods we support
261 #define SUPPORTED_COMPRESSION_METHODS session->internals.priorities.compression.algorithms
263 _gnutls_supported_compression_methods (gnutls_session_t session,
264 uint8_t ** comp)
266 unsigned int i, j;
268 *comp = gnutls_malloc (sizeof (uint8_t) * SUPPORTED_COMPRESSION_METHODS);
269 if (*comp == NULL)
270 return GNUTLS_E_MEMORY_ERROR;
272 for (i = j = 0; i < SUPPORTED_COMPRESSION_METHODS; i++)
274 int tmp =
275 _gnutls_compression_get_num (session->internals.
276 priorities.compression.priority[i]);
278 /* remove private compression algorithms, if requested.
280 if (tmp == -1 || (tmp >= MIN_PRIVATE_COMP_ALGO &&
281 session->internals.enable_private == 0))
283 gnutls_assert ();
284 continue;
287 (*comp)[j] = (uint8_t) tmp;
288 j++;
291 if (j == 0)
293 gnutls_assert ();
294 gnutls_free (*comp);
295 *comp = NULL;
296 return GNUTLS_E_NO_COMPRESSION_ALGORITHMS;
298 return j;
302 #ifdef USE_LZO
303 #ifdef USE_MINILZO
304 /* Get the prototypes only. Since LZO is a GPLed library, the
305 * gnutls_global_init_extra() has to be called, before LZO compression
306 * can be used.
308 #include "../libextra/minilzo/minilzo.h"
309 #elif HAVE_LZO_LZO1X_H
310 #include <lzo/lzo1x.h>
311 #elif HAVE_LZO1X_H
312 #include <lzo1x.h>
313 #endif
315 typedef int (*LZO_FUNC) ();
317 LZO_FUNC _gnutls_lzo1x_decompress_safe = NULL;
318 LZO_FUNC _gnutls_lzo1x_1_compress = NULL;
320 #endif
322 /* The flag d is the direction (compress, decompress). Non zero is
323 * decompress.
325 comp_hd_t
326 _gnutls_comp_init (gnutls_compression_method_t method, int d)
328 comp_hd_t ret;
330 ret = gnutls_malloc (sizeof (struct comp_hd_t_STRUCT));
331 if (ret == NULL)
333 gnutls_assert ();
334 return NULL;
337 ret->algo = method;
338 ret->handle = NULL;
340 switch (method)
342 case GNUTLS_COMP_DEFLATE:
343 #ifdef HAVE_LIBZ
345 int window_bits, mem_level;
346 int comp_level;
347 z_stream *zhandle;
348 int err;
350 window_bits = get_wbits (method);
351 mem_level = get_mem_level (method);
352 comp_level = get_comp_level (method);
354 ret->handle = gnutls_malloc (sizeof (z_stream));
355 if (ret->handle == NULL)
357 gnutls_assert ();
358 goto cleanup_ret;
361 zhandle = ret->handle;
363 zhandle->zalloc = (alloc_func) 0;
364 zhandle->zfree = (free_func) 0;
365 zhandle->opaque = (voidpf) 0;
367 if (d)
368 err = inflateInit2 (zhandle, window_bits);
369 else
371 err = deflateInit2 (zhandle,
372 comp_level, Z_DEFLATED,
373 window_bits, mem_level, Z_DEFAULT_STRATEGY);
375 if (err != Z_OK)
377 gnutls_assert ();
378 gnutls_free (ret->handle);
379 goto cleanup_ret;
382 break;
383 #endif
384 case GNUTLS_COMP_LZO:
385 #ifdef USE_LZO
386 /* LZO does not use memory on decompressor */
387 if (!d)
389 ret->handle = gnutls_malloc (LZO1X_1_MEM_COMPRESS);
391 if (ret->handle == NULL)
393 gnutls_assert ();
394 goto cleanup_ret;
397 break;
398 #endif
399 case GNUTLS_COMP_NULL:
400 case GNUTLS_COMP_UNKNOWN:
401 break;
404 return ret;
406 cleanup_ret:
407 gnutls_free (ret);
408 return NULL;
411 /* The flag d is the direction (compress, decompress). Non zero is
412 * decompress.
414 void
415 _gnutls_comp_deinit (comp_hd_t handle, int d)
417 if (handle != NULL)
419 switch (handle->algo)
421 #ifdef HAVE_LIBZ
422 case GNUTLS_COMP_DEFLATE:
424 if (d)
425 inflateEnd (handle->handle);
426 else
427 deflateEnd (handle->handle);
428 break;
430 #endif
431 default:
432 break;
434 gnutls_free (handle->handle);
435 gnutls_free (handle);
440 /* These functions are memory consuming
444 _gnutls_compress (comp_hd_t handle, const opaque * plain,
445 size_t plain_size, opaque ** compressed,
446 size_t max_comp_size)
448 int compressed_size = GNUTLS_E_COMPRESSION_FAILED;
450 /* NULL compression is not handled here
452 if (handle == NULL)
454 gnutls_assert ();
455 return GNUTLS_E_INTERNAL_ERROR;
458 switch (handle->algo)
460 #ifdef USE_LZO
461 case GNUTLS_COMP_LZO:
463 lzo_uint out_len;
464 size_t size;
465 int err;
467 if (_gnutls_lzo1x_1_compress == NULL)
468 return GNUTLS_E_COMPRESSION_FAILED;
470 size = plain_size + plain_size / 64 + 16 + 3;
471 *compressed = gnutls_malloc (size);
472 if (*compressed == NULL)
474 gnutls_assert ();
475 return GNUTLS_E_MEMORY_ERROR;
478 err = _gnutls_lzo1x_1_compress (plain, plain_size, *compressed,
479 &out_len, handle->handle);
481 if (err != LZO_E_OK)
483 gnutls_assert ();
484 gnutls_free (*compressed);
485 *compressed = NULL;
486 return GNUTLS_E_COMPRESSION_FAILED;
489 compressed_size = out_len;
490 break;
492 #endif
493 #ifdef HAVE_LIBZ
494 case GNUTLS_COMP_DEFLATE:
496 uLongf size;
497 z_stream *zhandle;
498 int err;
500 size = (plain_size + plain_size) + 10;
501 *compressed = gnutls_malloc (size);
502 if (*compressed == NULL)
504 gnutls_assert ();
505 return GNUTLS_E_MEMORY_ERROR;
508 zhandle = handle->handle;
510 zhandle->next_in = (Bytef *) plain;
511 zhandle->avail_in = plain_size;
512 zhandle->next_out = (Bytef *) * compressed;
513 zhandle->avail_out = size;
515 err = deflate (zhandle, Z_SYNC_FLUSH);
517 if (err != Z_OK || zhandle->avail_in != 0)
519 gnutls_assert ();
520 gnutls_free (*compressed);
521 *compressed = NULL;
522 return GNUTLS_E_COMPRESSION_FAILED;
525 compressed_size = size - zhandle->avail_out;
526 break;
528 #endif
529 default:
530 gnutls_assert ();
531 return GNUTLS_E_INTERNAL_ERROR;
532 } /* switch */
534 #ifdef COMPRESSION_DEBUG
535 _gnutls_debug_log ("Compression ratio: %f\n",
536 (float) ((float) compressed_size / (float) plain_size));
537 #endif
539 if ((size_t) compressed_size > max_comp_size)
541 gnutls_free (*compressed);
542 *compressed = NULL;
543 return GNUTLS_E_COMPRESSION_FAILED;
546 return compressed_size;
552 _gnutls_decompress (comp_hd_t handle, opaque * compressed,
553 size_t compressed_size, opaque ** plain,
554 size_t max_record_size)
556 int plain_size = GNUTLS_E_DECOMPRESSION_FAILED;
558 if (compressed_size > max_record_size + EXTRA_COMP_SIZE)
560 gnutls_assert ();
561 return GNUTLS_E_DECOMPRESSION_FAILED;
564 /* NULL compression is not handled here
567 if (handle == NULL)
569 gnutls_assert ();
570 return GNUTLS_E_INTERNAL_ERROR;
573 switch (handle->algo)
575 #ifdef USE_LZO
576 case GNUTLS_COMP_LZO:
578 lzo_uint out_size;
579 lzo_uint new_size;
580 int err;
582 if (_gnutls_lzo1x_decompress_safe == NULL)
583 return GNUTLS_E_DECOMPRESSION_FAILED;
585 *plain = NULL;
586 out_size = compressed_size + compressed_size;
587 plain_size = 0;
591 out_size += 512;
592 *plain = gnutls_realloc_fast (*plain, out_size);
593 if (*plain == NULL)
595 gnutls_assert ();
596 return GNUTLS_E_MEMORY_ERROR;
599 new_size = out_size;
600 err =
601 _gnutls_lzo1x_decompress_safe (compressed,
602 compressed_size, *plain,
603 &new_size, NULL);
606 while ((err == LZO_E_OUTPUT_OVERRUN && out_size < max_record_size));
608 if (err != LZO_E_OK)
610 gnutls_assert ();
611 gnutls_free (*plain);
612 *plain = NULL;
613 return GNUTLS_E_DECOMPRESSION_FAILED;
616 plain_size = new_size;
617 break;
619 #endif
620 #ifdef HAVE_LIBZ
621 case GNUTLS_COMP_DEFLATE:
623 uLongf out_size;
624 z_stream *zhandle;
625 int cur_pos;
626 int err;
628 *plain = NULL;
629 out_size = compressed_size + compressed_size;
630 plain_size = 0;
632 zhandle = handle->handle;
634 zhandle->next_in = (Bytef *) compressed;
635 zhandle->avail_in = compressed_size;
637 cur_pos = 0;
641 out_size += 512;
642 *plain = gnutls_realloc_fast (*plain, out_size);
643 if (*plain == NULL)
645 gnutls_assert ();
646 return GNUTLS_E_MEMORY_ERROR;
649 zhandle->next_out = (Bytef *) (*plain + cur_pos);
650 zhandle->avail_out = out_size - cur_pos;
652 err = inflate (zhandle, Z_SYNC_FLUSH);
654 cur_pos = out_size - zhandle->avail_out;
657 while ((err == Z_BUF_ERROR && zhandle->avail_out == 0
658 && out_size < max_record_size)
659 || (err == Z_OK && zhandle->avail_in != 0));
661 if (err != Z_OK)
663 gnutls_assert ();
664 gnutls_free (*plain);
665 *plain = NULL;
666 return GNUTLS_E_DECOMPRESSION_FAILED;
669 plain_size = out_size - zhandle->avail_out;
670 break;
672 #endif
673 default:
674 gnutls_assert ();
675 return GNUTLS_E_INTERNAL_ERROR;
676 } /* switch */
678 if ((size_t) plain_size > max_record_size)
680 gnutls_assert ();
681 gnutls_free (*plain);
682 *plain = NULL;
683 return GNUTLS_E_DECOMPRESSION_FAILED;
686 return plain_size;