Coverage Report

Created: 2024-02-25 06:27

/src/gnutls/lib/compress.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2017-2022 Red Hat, Inc.
3
 *
4
 * Authors: Nikos Mavrogiannopoulos,
5
 *          Zoltan Fridrich
6
 *
7
 * This file is part of GnuTLS.
8
 *
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.
13
 *
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.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public License
20
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
21
 *
22
 */
23
24
#include "config.h"
25
26
#include "compress.h"
27
28
#ifndef _WIN32
29
#include <dlfcn.h>
30
#endif
31
32
#ifdef HAVE_LIBZ
33
#include <zlib.h>
34
#endif
35
36
#ifdef HAVE_LIBBROTLI
37
#include <brotli/decode.h>
38
#include <brotli/encode.h>
39
#endif
40
41
#ifdef HAVE_LIBZSTD
42
#include <zstd.h>
43
#endif
44
45
#ifdef HAVE_LIBZ
46
static void *_zlib_handle;
47
48
#if HAVE___TYPEOF__
49
static __typeof__(compressBound)(*_gnutls_zlib_compressBound);
50
static __typeof__(compress)(*_gnutls_zlib_compress);
51
static __typeof__(uncompress)(*_gnutls_zlib_uncompress);
52
#else
53
static uLong (*_gnutls_zlib_compressBound)(uLong sourceLen);
54
static int (*_gnutls_zlib_compress)(Bytef *dest, uLongf *destLen,
55
            const Bytef *source, uLong sourceLen);
56
static int (*_gnutls_zlib_uncompress)(Bytef *dest, uLongf *destLen,
57
              const Bytef *source, uLong sourceLen);
58
#endif /* HAVE___TYPEOF__ */
59
60
static void zlib_deinit(void)
61
{
62
#ifndef _WIN32
63
  if (_zlib_handle != NULL) {
64
    dlclose(_zlib_handle);
65
    _zlib_handle = NULL;
66
  }
67
#endif /* _WIN32 */
68
}
69
70
static int zlib_init(void)
71
{
72
#ifndef _WIN32
73
  if (_zlib_handle != NULL)
74
    return 0;
75
  if ((_zlib_handle = dlopen("libz.so.1", RTLD_NOW | RTLD_GLOBAL)) ==
76
      NULL)
77
    goto error;
78
  if ((_gnutls_zlib_compressBound =
79
         dlsym(_zlib_handle, "compressBound")) == NULL)
80
    goto error;
81
  if ((_gnutls_zlib_compress = dlsym(_zlib_handle, "compress")) == NULL)
82
    goto error;
83
  if ((_gnutls_zlib_uncompress = dlsym(_zlib_handle, "uncompress")) ==
84
      NULL)
85
    goto error;
86
  return 0;
87
error:
88
  zlib_deinit();
89
  return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
90
#else
91
  return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
92
#endif /* _WIN32 */
93
}
94
#endif /* HAVE_LIBZ */
95
96
#ifdef HAVE_LIBBROTLI
97
static void *_brotlienc_handle;
98
static void *_brotlidec_handle;
99
100
#if HAVE___TYPEOF__
101
static __typeof__(BrotliEncoderMaxCompressedSize)(
102
  *_gnutls_BrotliEncoderMaxCompressedSize);
103
static __typeof__(BrotliEncoderCompress)(*_gnutls_BrotliEncoderCompress);
104
static __typeof__(BrotliDecoderDecompress)(*_gnutls_BrotliDecoderDecompress);
105
#else
106
static size_t (*_gnutls_BrotliEncoderMaxCompressedSize)(size_t input_size);
107
static BROTLI_BOOL (*_gnutls_BrotliEncoderCompress)(
108
  int quality, int lgwin, BrotliEncoderMode mode, size_t input_size,
109
  const uint8_t input_buffer[BROTLI_ARRAY_PARAM(input_size)],
110
  size_t *encoded_size,
111
  uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(*encoded_size)]);
112
static BrotliDecoderResult (*_gnutls_BrotliDecoderDecompress)(
113
  size_t encoded_size,
114
  const uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(encoded_size)],
115
  size_t *decoded_size,
116
  uint8_t decoded_buffer[BROTLI_ARRAY_PARAM(*decoded_size)]);
117
#endif /* HAVE___TYPEOF__ */
118
119
static void brotli_deinit(void)
120
{
121
#ifndef _WIN32
122
  if (_brotlienc_handle != NULL) {
123
    dlclose(_brotlienc_handle);
124
    _brotlienc_handle = NULL;
125
  }
126
  if (_brotlidec_handle != NULL) {
127
    dlclose(_brotlidec_handle);
128
    _brotlidec_handle = NULL;
129
  }
130
#endif /* _WIN32 */
131
}
132
133
static int brotli_init(void)
134
{
135
#ifndef _WIN32
136
  if (_brotlienc_handle != NULL || _brotlidec_handle != NULL)
137
    return 0;
138
  if ((_brotlienc_handle = dlopen("libbrotlienc.so.1",
139
          RTLD_NOW | RTLD_GLOBAL)) == NULL)
140
    goto error;
141
  if ((_brotlidec_handle = dlopen("libbrotlidec.so.1",
142
          RTLD_NOW | RTLD_GLOBAL)) == NULL)
143
    goto error;
144
  if ((_gnutls_BrotliEncoderMaxCompressedSize =
145
         dlsym(_brotlienc_handle,
146
         "BrotliEncoderMaxCompressedSize")) == NULL)
147
    goto error;
148
  if ((_gnutls_BrotliEncoderCompress =
149
         dlsym(_brotlienc_handle, "BrotliEncoderCompress")) == NULL)
150
    goto error;
151
  if ((_gnutls_BrotliDecoderDecompress = dlsym(
152
         _brotlidec_handle, "BrotliDecoderDecompress")) == NULL)
153
    goto error;
154
  return 0;
155
error:
156
  brotli_deinit();
157
  return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
158
#else
159
  return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
160
#endif /* _WIN32 */
161
}
162
#endif /* HAVE_LIBBROTLI */
163
164
#ifdef HAVE_LIBZSTD
165
static void *_zstd_handle;
166
167
#if HAVE___TYPEOF__
168
static __typeof__(ZSTD_isError)(*_gnutls_ZSTD_isError);
169
static __typeof__(ZSTD_compressBound)(*_gnutls_ZSTD_compressBound);
170
static __typeof__(ZSTD_compress)(*_gnutls_ZSTD_compress);
171
static __typeof__(ZSTD_decompress)(*_gnutls_ZSTD_decompress);
172
#else
173
static unsigned (*_gnutls_ZSTD_isError)(size_t code);
174
static size_t (*_gnutls_ZSTD_compressBound)(size_t srcSize);
175
static size_t (*_gnutls_ZSTD_compress)(void *dst, size_t dstCapacity,
176
               const void *src, size_t srcSize,
177
               int compressionLevel);
178
static size_t (*_gnutls_ZSTD_decompress)(void *dst, size_t dstCapacity,
179
           const void *src,
180
           size_t compressedSize);
181
#endif /* HAVE___TYPEOF__ */
182
183
static void zstd_deinit(void)
184
{
185
#ifndef _WIN32
186
  if (_zstd_handle != NULL) {
187
    dlclose(_zstd_handle);
188
    _zstd_handle = NULL;
189
  }
190
#endif /* _WIN32 */
191
}
192
193
static int zstd_init(void)
194
{
195
#ifndef _WIN32
196
  if (_zstd_handle != NULL)
197
    return 0;
198
  if ((_zstd_handle = dlopen("libzstd.so.1", RTLD_NOW | RTLD_GLOBAL)) ==
199
      NULL)
200
    goto error;
201
  if ((_gnutls_ZSTD_isError = dlsym(_zstd_handle, "ZSTD_isError")) ==
202
      NULL)
203
    goto error;
204
  if ((_gnutls_ZSTD_compressBound =
205
         dlsym(_zstd_handle, "ZSTD_compressBound")) == NULL)
206
    goto error;
207
  if ((_gnutls_ZSTD_compress = dlsym(_zstd_handle, "ZSTD_compress")) ==
208
      NULL)
209
    goto error;
210
  if ((_gnutls_ZSTD_decompress =
211
         dlsym(_zstd_handle, "ZSTD_decompress")) == NULL)
212
    goto error;
213
  return 0;
214
error:
215
  zstd_deinit();
216
  return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
217
#else
218
  return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
219
#endif /* _WIN32 */
220
}
221
#endif /* HAVE_LIBZSTD */
222
223
typedef struct {
224
  gnutls_compression_method_t id;
225
  const char *name;
226
  int (*init)(void);
227
  void (*deinit)(void);
228
} comp_entry;
229
230
static comp_entry comp_algs[] = {
231
  { GNUTLS_COMP_NULL, "NULL", NULL, NULL },
232
#ifdef HAVE_LIBZ
233
  { GNUTLS_COMP_ZLIB, "ZLIB", zlib_init, zlib_deinit },
234
#endif
235
#ifdef HAVE_LIBBROTLI
236
  { GNUTLS_COMP_BROTLI, "BROTLI", brotli_init, brotli_deinit },
237
#endif
238
#ifdef HAVE_LIBZSTD
239
  { GNUTLS_COMP_ZSTD, "ZSTD", zstd_init, zstd_deinit },
240
#endif
241
  { GNUTLS_COMP_UNKNOWN, NULL, NULL, NULL }
242
};
243
244
static const gnutls_compression_method_t alg_list[] = { GNUTLS_COMP_NULL,
245
#ifdef HAVE_LIBZ
246
              GNUTLS_COMP_ZLIB,
247
#endif
248
#ifdef HAVE_LIBBROTLI
249
              GNUTLS_COMP_BROTLI,
250
#endif
251
#ifdef HAVE_LIBZSTD
252
              GNUTLS_COMP_ZSTD,
253
#endif
254
              0 };
255
256
/* Initialize given compression method
257
 *
258
 * Calling any of the compression functions without first initializing
259
 * the respective compression method results in undefined behavior.
260
 */
261
int _gnutls_compression_init_method(gnutls_compression_method_t method)
262
0
{
263
0
  comp_entry *p;
264
265
0
  for (p = comp_algs; p->name; ++p)
266
0
    if (p->id == method)
267
0
      return p->init ? p->init() : GNUTLS_E_INVALID_REQUEST;
268
269
0
  return GNUTLS_E_INVALID_REQUEST;
270
0
}
271
272
/* Deinitialize all compression methods
273
 * 
274
 * If no compression methods were initialized,
275
 * this function does nothing.
276
 */
277
void _gnutls_compression_deinit(void)
278
0
{
279
0
  comp_entry *p;
280
281
0
  for (p = comp_algs; p->name; ++p)
282
0
    if (p->deinit)
283
0
      p->deinit();
284
0
}
285
286
/**
287
 * gnutls_compression_get_name:
288
 * @algorithm: is a Compression algorithm
289
 *
290
 * Convert a #gnutls_compression_method_t value to a string.
291
 *
292
 * Returns: a pointer to a string that contains the name of the
293
 *   specified compression algorithm, or %NULL.
294
 **/
295
const char *gnutls_compression_get_name(gnutls_compression_method_t algorithm)
296
0
{
297
0
  const comp_entry *p;
298
299
0
  for (p = comp_algs; p->name; ++p)
300
0
    if (p->id == algorithm)
301
0
      return p->name;
302
303
0
  return NULL;
304
0
}
305
306
/**
307
 * gnutls_compression_get_id:
308
 * @name: is a compression method name
309
 *
310
 * The names are compared in a case insensitive way.
311
 *
312
 * Returns: an id of the specified in a string compression method, or
313
 *   %GNUTLS_COMP_UNKNOWN on error.
314
 **/
315
gnutls_compression_method_t gnutls_compression_get_id(const char *name)
316
0
{
317
0
  const comp_entry *p;
318
319
0
  for (p = comp_algs; p->name; ++p)
320
0
    if (!strcasecmp(p->name, name))
321
0
      return p->id;
322
323
0
  return GNUTLS_COMP_UNKNOWN;
324
0
}
325
326
/**
327
 * gnutls_compression_list:
328
 *
329
 * Get a list of compression methods.
330
 *
331
 * Returns: a zero-terminated list of #gnutls_compression_method_t
332
 *   integers indicating the available compression methods.
333
 **/
334
const gnutls_compression_method_t *gnutls_compression_list(void)
335
0
{
336
0
  return alg_list;
337
0
}
338
339
/*************************/
340
/* Compression functions */
341
/*************************/
342
343
size_t _gnutls_compress_bound(gnutls_compression_method_t alg, size_t src_len)
344
0
{
345
0
  switch (alg) {
346
#ifdef HAVE_LIBZ
347
  case GNUTLS_COMP_ZLIB:
348
    return _gnutls_zlib_compressBound(src_len);
349
#endif
350
#ifdef HAVE_LIBBROTLI
351
  case GNUTLS_COMP_BROTLI:
352
    return _gnutls_BrotliEncoderMaxCompressedSize(src_len);
353
#endif
354
#ifdef HAVE_LIBZSTD
355
  case GNUTLS_COMP_ZSTD:
356
    return _gnutls_ZSTD_compressBound(src_len);
357
#endif
358
0
  default:
359
0
    return 0;
360
0
  }
361
0
  return 0;
362
0
}
363
364
int _gnutls_compress(gnutls_compression_method_t alg, uint8_t *dst,
365
         size_t dst_len, const uint8_t *src, size_t src_len)
366
0
{
367
0
  int ret = GNUTLS_E_COMPRESSION_FAILED;
368
369
0
  switch (alg) {
370
#ifdef HAVE_LIBZ
371
  case GNUTLS_COMP_ZLIB: {
372
    int err;
373
    uLongf comp_len = dst_len;
374
375
    err = _gnutls_zlib_compress(dst, &comp_len, src, src_len);
376
    if (err != Z_OK)
377
      return gnutls_assert_val(GNUTLS_E_COMPRESSION_FAILED);
378
    ret = comp_len;
379
  } break;
380
#endif
381
#ifdef HAVE_LIBBROTLI
382
  case GNUTLS_COMP_BROTLI: {
383
    BROTLI_BOOL err;
384
    size_t comp_len = dst_len;
385
386
    err = _gnutls_BrotliEncoderCompress(
387
      BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW,
388
      BROTLI_DEFAULT_MODE, src_len, src, &comp_len, dst);
389
    if (!err)
390
      return gnutls_assert_val(GNUTLS_E_COMPRESSION_FAILED);
391
    ret = comp_len;
392
  } break;
393
#endif
394
#ifdef HAVE_LIBZSTD
395
  case GNUTLS_COMP_ZSTD: {
396
    size_t comp_len;
397
398
    comp_len = _gnutls_ZSTD_compress(dst, dst_len, src, src_len,
399
             ZSTD_CLEVEL_DEFAULT);
400
    if (_gnutls_ZSTD_isError(comp_len))
401
      return gnutls_assert_val(GNUTLS_E_COMPRESSION_FAILED);
402
    ret = comp_len;
403
  } break;
404
#endif
405
0
  default:
406
0
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
407
0
  }
408
409
#ifdef COMPRESSION_DEBUG
410
  _gnutls_debug_log("Compression ratio: %f\n",
411
        (float)((float)ret / (float)src_len));
412
#endif
413
414
0
  return ret;
415
0
}
416
417
int _gnutls_decompress(gnutls_compression_method_t alg, uint8_t *dst,
418
           size_t dst_len, const uint8_t *src, size_t src_len)
419
0
{
420
0
  int ret = GNUTLS_E_DECOMPRESSION_FAILED;
421
422
0
  switch (alg) {
423
#ifdef HAVE_LIBZ
424
  case GNUTLS_COMP_ZLIB: {
425
    int err;
426
    uLongf plain_len = dst_len;
427
428
    err = _gnutls_zlib_uncompress(dst, &plain_len, src, src_len);
429
    if (err != Z_OK)
430
      return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED);
431
    ret = plain_len;
432
  } break;
433
#endif
434
#ifdef HAVE_LIBBROTLI
435
  case GNUTLS_COMP_BROTLI: {
436
    BrotliDecoderResult err;
437
    size_t plain_len = dst_len;
438
439
    err = _gnutls_BrotliDecoderDecompress(src_len, src, &plain_len,
440
                  dst);
441
    if (err != BROTLI_DECODER_RESULT_SUCCESS)
442
      return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED);
443
    ret = plain_len;
444
  } break;
445
#endif
446
#ifdef HAVE_LIBZSTD
447
  case GNUTLS_COMP_ZSTD: {
448
    size_t plain_len;
449
450
    plain_len = _gnutls_ZSTD_decompress(dst, dst_len, src, src_len);
451
    if (_gnutls_ZSTD_isError(plain_len))
452
      return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED);
453
    ret = plain_len;
454
  } break;
455
#endif
456
0
  default:
457
0
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
458
0
  }
459
460
0
  return ret;
461
0
}