Coverage Report

Created: 2025-03-06 06:58

/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
#ifdef _WIN32
29
#define RTLD_NOW 0
30
#define RTLD_GLOBAL 0
31
#else
32
#include <dlfcn.h>
33
#endif
34
35
#ifndef Z_LIBRARY_SONAME
36
#define Z_LIBRARY_SONAME "none"
37
#endif
38
39
#ifndef BROTLIENC_LIBRARY_SONAME
40
#define BROTLIENC_LIBRARY_SONAME "none"
41
#endif
42
43
#ifndef BROTLIDEC_LIBRARY_SONAME
44
#define BROTLIDEC_LIBRARY_SONAME "none"
45
#endif
46
47
#ifndef ZSTD_LIBRARY_SONAME
48
#define ZSTD_LIBRARY_SONAME "none"
49
#endif
50
51
#ifdef HAVE_ZLIB
52
#include "dlwrap/zlib.h"
53
#endif
54
55
#ifdef HAVE_LIBBROTLI
56
#include "dlwrap/brotlienc.h"
57
#include "dlwrap/brotlidec.h"
58
#endif
59
60
#ifdef HAVE_LIBZSTD
61
#include "dlwrap/zstd.h"
62
#endif
63
64
#ifdef HAVE_ZLIB
65
static void zlib_deinit(void)
66
0
{
67
0
  gnutls_zlib_unload_library();
68
0
}
69
70
static int zlib_init(void)
71
0
{
72
0
  if (gnutls_zlib_ensure_library(Z_LIBRARY_SONAME,
73
0
               RTLD_NOW | RTLD_GLOBAL) < 0)
74
0
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
75
0
  return 0;
76
0
}
77
#endif /* HAVE_ZLIB */
78
79
#ifdef HAVE_LIBBROTLI
80
81
static void brotli_deinit(void)
82
{
83
  gnutls_brotlienc_unload_library();
84
  gnutls_brotlidec_unload_library();
85
}
86
87
static int brotli_init(void)
88
{
89
  if (gnutls_brotlienc_ensure_library(BROTLIENC_LIBRARY_SONAME,
90
              RTLD_NOW | RTLD_GLOBAL) < 0)
91
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
92
93
  if (gnutls_brotlidec_ensure_library(BROTLIDEC_LIBRARY_SONAME,
94
              RTLD_NOW | RTLD_GLOBAL) < 0)
95
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
96
  return 0;
97
}
98
#endif /* HAVE_LIBBROTLI */
99
100
#ifdef HAVE_LIBZSTD
101
102
static void zstd_deinit(void)
103
{
104
  gnutls_zstd_unload_library();
105
}
106
107
static int zstd_init(void)
108
{
109
  if (gnutls_zstd_ensure_library(ZSTD_LIBRARY_SONAME,
110
               RTLD_NOW | RTLD_GLOBAL) < 0)
111
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
112
  return 0;
113
}
114
#endif /* HAVE_LIBZSTD */
115
116
typedef struct {
117
  gnutls_compression_method_t id;
118
  const char *name;
119
  int (*init)(void);
120
  void (*deinit)(void);
121
} comp_entry;
122
123
static comp_entry comp_algs[] = {
124
  { GNUTLS_COMP_NULL, "NULL", NULL, NULL },
125
#ifdef HAVE_ZLIB
126
  { GNUTLS_COMP_ZLIB, "ZLIB", zlib_init, zlib_deinit },
127
#endif
128
#ifdef HAVE_LIBBROTLI
129
  { GNUTLS_COMP_BROTLI, "BROTLI", brotli_init, brotli_deinit },
130
#endif
131
#ifdef HAVE_LIBZSTD
132
  { GNUTLS_COMP_ZSTD, "ZSTD", zstd_init, zstd_deinit },
133
#endif
134
  { GNUTLS_COMP_UNKNOWN, NULL, NULL, NULL }
135
};
136
137
static const gnutls_compression_method_t alg_list[] = { GNUTLS_COMP_NULL,
138
#ifdef HAVE_ZLIB
139
              GNUTLS_COMP_ZLIB,
140
#endif
141
#ifdef HAVE_LIBBROTLI
142
              GNUTLS_COMP_BROTLI,
143
#endif
144
#ifdef HAVE_LIBZSTD
145
              GNUTLS_COMP_ZSTD,
146
#endif
147
              0 };
148
149
/* Initialize given compression method
150
 *
151
 * Calling any of the compression functions without first initializing
152
 * the respective compression method results in undefined behavior.
153
 */
154
int _gnutls_compression_init_method(gnutls_compression_method_t method)
155
0
{
156
0
  comp_entry *p;
157
158
0
  for (p = comp_algs; p->name; ++p)
159
0
    if (p->id == method)
160
0
      return p->init ? p->init() : GNUTLS_E_INVALID_REQUEST;
161
162
0
  return GNUTLS_E_INVALID_REQUEST;
163
0
}
164
165
/* Deinitialize all compression methods
166
 * 
167
 * If no compression methods were initialized,
168
 * this function does nothing.
169
 */
170
void _gnutls_compression_deinit(void)
171
0
{
172
0
  comp_entry *p;
173
174
0
  for (p = comp_algs; p->name; ++p)
175
0
    if (p->deinit)
176
0
      p->deinit();
177
0
}
178
179
/**
180
 * gnutls_compression_get_name:
181
 * @algorithm: is a Compression algorithm
182
 *
183
 * Convert a #gnutls_compression_method_t value to a string.
184
 *
185
 * Returns: a pointer to a string that contains the name of the
186
 *   specified compression algorithm, or %NULL.
187
 **/
188
const char *gnutls_compression_get_name(gnutls_compression_method_t algorithm)
189
0
{
190
0
  const comp_entry *p;
191
192
0
  for (p = comp_algs; p->name; ++p)
193
0
    if (p->id == algorithm)
194
0
      return p->name;
195
196
0
  return NULL;
197
0
}
198
199
/**
200
 * gnutls_compression_get_id:
201
 * @name: is a compression method name
202
 *
203
 * The names are compared in a case insensitive way.
204
 *
205
 * Returns: an id of the specified in a string compression method, or
206
 *   %GNUTLS_COMP_UNKNOWN on error.
207
 **/
208
gnutls_compression_method_t gnutls_compression_get_id(const char *name)
209
0
{
210
0
  const comp_entry *p;
211
212
0
  for (p = comp_algs; p->name; ++p)
213
0
    if (!strcasecmp(p->name, name))
214
0
      return p->id;
215
216
0
  return GNUTLS_COMP_UNKNOWN;
217
0
}
218
219
/**
220
 * gnutls_compression_list:
221
 *
222
 * Get a list of compression methods.
223
 *
224
 * Returns: a zero-terminated list of #gnutls_compression_method_t
225
 *   integers indicating the available compression methods.
226
 **/
227
const gnutls_compression_method_t *gnutls_compression_list(void)
228
0
{
229
0
  return alg_list;
230
0
}
231
232
/*************************/
233
/* Compression functions */
234
/*************************/
235
236
size_t _gnutls_compress_bound(gnutls_compression_method_t alg, size_t src_len)
237
0
{
238
0
  switch (alg) {
239
0
#ifdef HAVE_ZLIB
240
0
  case GNUTLS_COMP_ZLIB:
241
0
    return GNUTLS_ZLIB_FUNC(compressBound)(src_len);
242
0
#endif
243
#ifdef HAVE_LIBBROTLI
244
  case GNUTLS_COMP_BROTLI:
245
    return GNUTLS_BROTLIENC_FUNC(BrotliEncoderMaxCompressedSize)(
246
      src_len);
247
#endif
248
#ifdef HAVE_LIBZSTD
249
  case GNUTLS_COMP_ZSTD:
250
    return GNUTLS_ZSTD_FUNC(ZSTD_compressBound)(src_len);
251
#endif
252
0
  default:
253
0
    return 0;
254
0
  }
255
0
  return 0;
256
0
}
257
258
int _gnutls_compress(gnutls_compression_method_t alg, uint8_t *dst,
259
         size_t dst_len, const uint8_t *src, size_t src_len)
260
0
{
261
0
  int ret = GNUTLS_E_COMPRESSION_FAILED;
262
263
0
  switch (alg) {
264
0
#ifdef HAVE_ZLIB
265
0
  case GNUTLS_COMP_ZLIB: {
266
0
    int err;
267
0
    uLongf comp_len = dst_len;
268
269
0
    err = GNUTLS_ZLIB_FUNC(compress)(dst, &comp_len, src, src_len);
270
0
    if (err != Z_OK)
271
0
      return gnutls_assert_val(GNUTLS_E_COMPRESSION_FAILED);
272
0
    ret = comp_len;
273
0
  } break;
274
0
#endif
275
#ifdef HAVE_LIBBROTLI
276
  case GNUTLS_COMP_BROTLI: {
277
    BROTLI_BOOL err;
278
    size_t comp_len = dst_len;
279
280
    err = GNUTLS_BROTLIENC_FUNC(BrotliEncoderCompress)(
281
      BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW,
282
      BROTLI_DEFAULT_MODE, src_len, src, &comp_len, dst);
283
    if (!err)
284
      return gnutls_assert_val(GNUTLS_E_COMPRESSION_FAILED);
285
    ret = comp_len;
286
  } break;
287
#endif
288
#ifdef HAVE_LIBZSTD
289
  case GNUTLS_COMP_ZSTD: {
290
    size_t comp_len;
291
292
    comp_len = GNUTLS_ZSTD_FUNC(ZSTD_compress)(
293
      dst, dst_len, src, src_len, ZSTD_CLEVEL_DEFAULT);
294
    if (GNUTLS_ZSTD_FUNC(ZSTD_isError)(comp_len))
295
      return gnutls_assert_val(GNUTLS_E_COMPRESSION_FAILED);
296
    ret = comp_len;
297
  } break;
298
#endif
299
0
  default:
300
0
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
301
0
  }
302
303
#ifdef COMPRESSION_DEBUG
304
  _gnutls_debug_log("Compression ratio: %f\n",
305
        (float)((float)ret / (float)src_len));
306
#endif
307
308
0
  return ret;
309
0
}
310
311
int _gnutls_decompress(gnutls_compression_method_t alg, uint8_t *dst,
312
           size_t dst_len, const uint8_t *src, size_t src_len)
313
0
{
314
0
  int ret = GNUTLS_E_DECOMPRESSION_FAILED;
315
316
0
  switch (alg) {
317
0
#ifdef HAVE_ZLIB
318
0
  case GNUTLS_COMP_ZLIB: {
319
0
    int err;
320
0
    uLongf plain_len = dst_len;
321
322
0
    err = GNUTLS_ZLIB_FUNC(uncompress)(dst, &plain_len, src,
323
0
               src_len);
324
0
    if (err != Z_OK)
325
0
      return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED);
326
0
    ret = plain_len;
327
0
  } break;
328
0
#endif
329
#ifdef HAVE_LIBBROTLI
330
  case GNUTLS_COMP_BROTLI: {
331
    BrotliDecoderResult err;
332
    size_t plain_len = dst_len;
333
334
    err = GNUTLS_BROTLIDEC_FUNC(
335
      BrotliDecoderDecompress)(src_len, src, &plain_len, dst);
336
    if (err != BROTLI_DECODER_RESULT_SUCCESS)
337
      return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED);
338
    ret = plain_len;
339
  } break;
340
#endif
341
#ifdef HAVE_LIBZSTD
342
  case GNUTLS_COMP_ZSTD: {
343
    size_t plain_len;
344
345
    plain_len = GNUTLS_ZSTD_FUNC(ZSTD_decompress)(dst, dst_len, src,
346
                    src_len);
347
    if (GNUTLS_ZSTD_FUNC(ZSTD_isError)(plain_len))
348
      return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED);
349
    ret = plain_len;
350
  } break;
351
#endif
352
0
  default:
353
0
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
354
0
  }
355
356
0
  return ret;
357
0
}