1/*
2 * Copyright (C) 2000-2012 Free Software Foundation, Inc.
3 *
4 * Author: Nikos Mavrogiannopoulos
5 *
6 * This file is part of GnuTLS.
7 *
8 * The GnuTLS is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>
20 *
21 */
22
23/* This file contains the functions which convert the TLS plaintext
24 * packet to TLS compressed packet.
25 */
26
27#include "gnutls_int.h"
28#include "gnutls_compress.h"
29#include "gnutls_errors.h"
30#include "gnutls_constate.h"
31#include <algorithms.h>
32#include <gnutls/gnutls.h>
33
34/* Compression Section */
35#define GNUTLS_COMPRESSION_ENTRY(name, id, wb, ml, cl) \
36 { #name, name, id, wb, ml, cl}
37
38
39#define MAX_COMP_METHODS 5
40const int _gnutls_comp_algorithms_size = MAX_COMP_METHODS;
41
42gnutls_compression_entry _gnutls_compression_algorithms[MAX_COMP_METHODS] = {
43 GNUTLS_COMPRESSION_ENTRY (GNUTLS_COMP_NULL, 0x00, 0, 0, 0),
44#ifdef HAVE_LIBZ
45 /* draft-ietf-tls-compression-02 */
46 GNUTLS_COMPRESSION_ENTRY (GNUTLS_COMP_DEFLATE, 0x01, 15, 8, 3),
47#endif
48 {0, 0, 0, 0, 0, 0}
49};
50
51static const gnutls_compression_method_t supported_compressions[] = {
52#ifdef HAVE_LIBZ
53 GNUTLS_COMP_DEFLATE,
54#endif
55 GNUTLS_COMP_NULL,
56 0
57};
58
59#define GNUTLS_COMPRESSION_LOOP(b) \
60 const gnutls_compression_entry *p; \
61 for(p = _gnutls_compression_algorithms; p->name != NULL; p++) { b ; }
62#define GNUTLS_COMPRESSION_ALG_LOOP(a) \
63 GNUTLS_COMPRESSION_LOOP( if(p->id == algorithm) { a; break; } )
64#define GNUTLS_COMPRESSION_ALG_LOOP_NUM(a) \
65 GNUTLS_COMPRESSION_LOOP( if(p->num == num) { a; break; } )
66
67/* Compression Functions */
68
69/**
70 * gnutls_compression_get_name:
71 * @algorithm: is a Compression algorithm
72 *
73 * Convert a #gnutls_compression_method_t value to a string.
74 *
75 * Returns: a pointer to a string that contains the name of the
76 * specified compression algorithm, or %NULL.
77 **/
78const char *
79gnutls_compression_get_name (gnutls_compression_method_t algorithm)
80{
81 const char *ret = NULL;
82
83 /* avoid prefix */
84 GNUTLS_COMPRESSION_ALG_LOOP (ret = p->name + sizeof ("GNUTLS_COMP_") - 1);
85
86 return ret;
87}
88
89/**
90 * gnutls_compression_get_id:
91 * @name: is a compression method name
92 *
93 * The names are compared in a case insensitive way.
94 *
95 * Returns: an id of the specified in a string compression method, or
96 * %GNUTLS_COMP_UNKNOWN on error.
97 **/
98gnutls_compression_method_t
99gnutls_compression_get_id (const char *name)
100{
101 gnutls_compression_method_t ret = GNUTLS_COMP_UNKNOWN;
102
103 GNUTLS_COMPRESSION_LOOP (if
104 (strcasecmp
105 (p->name + sizeof ("GNUTLS_COMP_") - 1,
106 name) == 0) ret = p->id);
107
108 return ret;
109}
110
111/**
112 * gnutls_compression_list:
113 *
114 * Get a list of compression methods.
115 *
116 * Returns: a zero-terminated list of #gnutls_compression_method_t
117 * integers indicating the available compression methods.
118 **/
119const gnutls_compression_method_t *
120gnutls_compression_list (void)
121{
122 return supported_compressions;
123}
124
125/* return the tls number of the specified algorithm */
126int
127_gnutls_compression_get_num (gnutls_compression_method_t algorithm)
128{
129 int ret = -1;
130
131 /* avoid prefix */
132 GNUTLS_COMPRESSION_ALG_LOOP (ret = p->num);
133
134 return ret;
135}
136
137#ifdef HAVE_LIBZ
138
139static int
140get_wbits (gnutls_compression_method_t algorithm)
141{
142 int ret = -1;
143 /* avoid prefix */
144 GNUTLS_COMPRESSION_ALG_LOOP (ret = p->window_bits);
145 return ret;
146}
147
148static int
149get_mem_level (gnutls_compression_method_t algorithm)
150{
151 int ret = -1;
152 /* avoid prefix */
153 GNUTLS_COMPRESSION_ALG_LOOP (ret = p->mem_level);
154 return ret;
155}
156
157static int
158get_comp_level (gnutls_compression_method_t algorithm)
159{
160 int ret = -1;
161 /* avoid prefix */
162 GNUTLS_COMPRESSION_ALG_LOOP (ret = p->comp_level);
163 return ret;
164}
165
166#endif
167
168/* returns the gnutls internal ID of the TLS compression
169 * method num
170 */
171gnutls_compression_method_t
172_gnutls_compression_get_id (int num)
173{
174 gnutls_compression_method_t ret = -1;
175
176 /* avoid prefix */
177 GNUTLS_COMPRESSION_ALG_LOOP_NUM (ret = p->id);
178
179 return ret;
180}
181
182int
183_gnutls_compression_is_ok (gnutls_compression_method_t algorithm)
184{
185 ssize_t ret = -1;
186 GNUTLS_COMPRESSION_ALG_LOOP (ret = p->id);
187 if (ret >= 0)
188 ret = 0;
189 else
190 ret = 1;
191 return ret;
192}
193
194
195
196/* For compression */
197
198#define MIN_PRIVATE_COMP_ALGO 0xEF
199
200/* returns the TLS numbers of the compression methods we support
201 */
202#define SUPPORTED_COMPRESSION_METHODS session->internals.priorities.compression.algorithms
203int
204_gnutls_supported_compression_methods (gnutls_session_t session,
205 uint8_t * comp, size_t comp_size)
206{
207 unsigned int i, j;
208
209 if (comp_size < SUPPORTED_COMPRESSION_METHODS)
210 return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
211
212 for (i = j = 0; i < SUPPORTED_COMPRESSION_METHODS; i++)
213 {
214 int tmp =
215 _gnutls_compression_get_num (session->internals.
216 priorities.compression.priority[i]);
217
218 /* remove private compression algorithms, if requested.
219 */
220 if (tmp == -1 || (tmp >= MIN_PRIVATE_COMP_ALGO &&
221 session->internals.enable_private == 0))
222 {
223 gnutls_assert ();
224 continue;
225 }
226
227 comp[j] = (uint8_t) tmp;
228 j++;
229 }
230
231 if (j == 0)
232 {
233 gnutls_assert ();
234 return GNUTLS_E_NO_COMPRESSION_ALGORITHMS;
235 }
236 return j;
237}
238
239
240/* The flag d is the direction (compress, decompress). Non zero is
241 * decompress.
242 */
243int _gnutls_comp_init (comp_hd_st* handle, gnutls_compression_method_t method, int d)
244{
245 handle->algo = method;
246 handle->handle = NULL;
247
248 switch (method)
249 {
250 case GNUTLS_COMP_DEFLATE:
251#ifdef HAVE_LIBZ
252 {
253 int window_bits, mem_level;
254 int comp_level;
255 z_stream *zhandle;
256 int err;
257
258 window_bits = get_wbits (method);
259 mem_level = get_mem_level (method);
260 comp_level = get_comp_level (method);
261
262 handle->handle = gnutls_malloc (sizeof (z_stream));
263 if (handle->handle == NULL)
264 return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
265
266 zhandle = handle->handle;
267
268 zhandle->zalloc = (alloc_func) 0;
269 zhandle->zfree = (free_func) 0;
270 zhandle->opaque = (voidpf) 0;
271
272 if (d)
273 err = inflateInit2 (zhandle, window_bits);
274 else
275 {
276 err = deflateInit2 (zhandle,
277 comp_level, Z_DEFLATED,
278 window_bits, mem_level, Z_DEFAULT_STRATEGY);
279 }
280 if (err != Z_OK)
281 {
282 gnutls_assert ();
283 gnutls_free (handle->handle);
284 return GNUTLS_E_COMPRESSION_FAILED;
285 }
286 }
287 break;
288#endif
289 case GNUTLS_COMP_NULL:
290 case GNUTLS_COMP_UNKNOWN:
291 break;
292 default:
293 return GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM;
294 }
295
296 return 0;
297}
298
299/* The flag d is the direction (compress, decompress). Non zero is
300 * decompress.
301 */
302void
303_gnutls_comp_deinit (comp_hd_st* handle, int d)
304{
305 if (handle != NULL)
306 {
307 switch (handle->algo)
308 {
309#ifdef HAVE_LIBZ
310 case GNUTLS_COMP_DEFLATE:
311 {
312 if (d)
313 inflateEnd (handle->handle);
314 else
315 deflateEnd (handle->handle);
316 break;
317 }
318#endif
319 default:
320 break;
321 }
322 gnutls_free (handle->handle);
323 handle->handle = NULL;
324 }
325}
326
327/* These functions are memory consuming
328 */
329
330int
331_gnutls_compress (comp_hd_st *handle, const uint8_t * plain,
332 size_t plain_size, uint8_t * compressed,
333 size_t max_comp_size, unsigned int stateless)
334{
335 int compressed_size = GNUTLS_E_COMPRESSION_FAILED;
336
337 /* NULL compression is not handled here
338 */
339 if (handle == NULL)
340 {
341 gnutls_assert ();
342 return GNUTLS_E_INTERNAL_ERROR;
343 }
344
345 switch (handle->algo)
346 {
347#ifdef HAVE_LIBZ
348 case GNUTLS_COMP_DEFLATE:
349 {
350 z_stream *zhandle;
351 int err;
352 int type;
353
354 if (stateless)
355 {
356 type = Z_FULL_FLUSH;
357 }
358 else
359 type = Z_SYNC_FLUSH;
360
361 zhandle = handle->handle;
362
363 zhandle->next_in = (Bytef *) plain;
364 zhandle->avail_in = plain_size;
365 zhandle->next_out = (Bytef *) compressed;
366 zhandle->avail_out = max_comp_size;
367
368 err = deflate (zhandle, type);
369 if (err != Z_OK || zhandle->avail_in != 0)
370 return gnutls_assert_val(GNUTLS_E_COMPRESSION_FAILED);
371
372
373 compressed_size = max_comp_size - zhandle->avail_out;
374 break;
375 }
376#endif
377 default:
378 gnutls_assert ();
379 return GNUTLS_E_INTERNAL_ERROR;
380 } /* switch */
381
382#ifdef COMPRESSION_DEBUG
383 _gnutls_debug_log ("Compression ratio: %f\n",
384 (float) ((float) compressed_size / (float) plain_size));
385#endif
386
387 return compressed_size;
388}
389
390
391
392int
393_gnutls_decompress (comp_hd_st *handle, uint8_t * compressed,
394 size_t compressed_size, uint8_t * plain,
395 size_t max_plain_size)
396{
397 int plain_size = GNUTLS_E_DECOMPRESSION_FAILED;
398
399 if (compressed_size > max_plain_size + EXTRA_COMP_SIZE)
400 {
401 gnutls_assert ();
402 return GNUTLS_E_DECOMPRESSION_FAILED;
403 }
404
405 /* NULL compression is not handled here
406 */
407
408 if (handle == NULL)
409 {
410 gnutls_assert ();
411 return GNUTLS_E_INTERNAL_ERROR;
412 }
413
414 switch (handle->algo)
415 {
416#ifdef HAVE_LIBZ
417 case GNUTLS_COMP_DEFLATE:
418 {
419 z_stream *zhandle;
420 int err;
421
422 zhandle = handle->handle;
423
424 zhandle->next_in = (Bytef *) compressed;
425 zhandle->avail_in = compressed_size;
426
427 zhandle->next_out = (Bytef *) plain;
428 zhandle->avail_out = max_plain_size;
429 err = inflate (zhandle, Z_SYNC_FLUSH);
430
431 if (err != Z_OK)
432 return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED);
433
434 plain_size = max_plain_size - zhandle->avail_out;
435 break;
436 }
437#endif
438 default:
439 gnutls_assert ();
440 return GNUTLS_E_INTERNAL_ERROR;
441 } /* switch */
442
443 return plain_size;
444}
445