/src/openssl/crypto/comp/c_zstd.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 1998-2023 The OpenSSL Project Authors. All Rights Reserved. |
3 | | * |
4 | | * Licensed under the Apache License 2.0 (the "License"). You may not use |
5 | | * this file except in compliance with the License. You can obtain a copy |
6 | | * in the file LICENSE in the source distribution or at |
7 | | * https://www.openssl.org/source/license.html |
8 | | * |
9 | | * Uses zstd compression library from https://github.com/facebook/zstd |
10 | | * Requires version 1.4.x (latest as of this writing is 1.4.5) |
11 | | * Using custom free functions require static linking, so that is disabled when |
12 | | * using the shared library. |
13 | | */ |
14 | | |
15 | | #include <stdio.h> |
16 | | #include <stdlib.h> |
17 | | #include <string.h> |
18 | | #include <openssl/objects.h> |
19 | | #include "internal/comp.h" |
20 | | #include <openssl/err.h> |
21 | | #include "crypto/cryptlib.h" |
22 | | #include "internal/bio.h" |
23 | | #include "internal/thread_once.h" |
24 | | #include "comp_local.h" |
25 | | |
26 | | COMP_METHOD *COMP_zstd(void); |
27 | | |
28 | | #ifdef OPENSSL_NO_ZSTD |
29 | | # undef ZSTD_SHARED |
30 | | #else |
31 | | |
32 | | # ifndef ZSTD_SHARED |
33 | | # define ZSTD_STATIC_LINKING_ONLY |
34 | | # endif |
35 | | # include <zstd.h> |
36 | | |
37 | | /* Note: There is also a linux zstd.h file in the kernel source */ |
38 | | # ifndef ZSTD_H_235446 |
39 | | # error Wrong (i.e. linux) zstd.h included. |
40 | | # endif |
41 | | |
42 | | # if ZSTD_VERSION_MAJOR != 1 && ZSTD_VERSION_MINOR < 4 |
43 | | # error Expecting version 1.4 or greater of ZSTD |
44 | | # endif |
45 | | |
46 | | # ifndef ZSTD_SHARED |
47 | | /* memory allocations functions for zstd initialisation */ |
48 | | static void *zstd_alloc(void *opaque, size_t size) |
49 | | { |
50 | | return OPENSSL_zalloc(size); |
51 | | } |
52 | | |
53 | | static void zstd_free(void *opaque, void *address) |
54 | | { |
55 | | OPENSSL_free(address); |
56 | | } |
57 | | |
58 | | static ZSTD_customMem zstd_mem_funcs = { |
59 | | zstd_alloc, |
60 | | zstd_free, |
61 | | NULL |
62 | | }; |
63 | | # endif |
64 | | |
65 | | /* |
66 | | * When OpenSSL is built on Windows, we do not want to require that |
67 | | * the LIBZSTD.DLL be available in order for the OpenSSL DLLs to |
68 | | * work. Therefore, all ZSTD routines are loaded at run time |
69 | | * and we do not link to a .LIB file when ZSTD_SHARED is set. |
70 | | */ |
71 | | # if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) |
72 | | # include <windows.h> |
73 | | # endif |
74 | | |
75 | | # ifdef ZSTD_SHARED |
76 | | # include "internal/dso.h" |
77 | | |
78 | | /* Function pointers */ |
79 | | typedef ZSTD_CStream* (*createCStream_ft)(void); |
80 | | typedef size_t (*initCStream_ft)(ZSTD_CStream*, int); |
81 | | typedef size_t (*freeCStream_ft)(ZSTD_CStream*); |
82 | | typedef size_t (*compressStream2_ft)(ZSTD_CCtx*, ZSTD_outBuffer*, ZSTD_inBuffer*, ZSTD_EndDirective); |
83 | | typedef size_t (*flushStream_ft)(ZSTD_CStream*, ZSTD_outBuffer*); |
84 | | typedef size_t (*endStream_ft)(ZSTD_CStream*, ZSTD_outBuffer*); |
85 | | typedef size_t (*compress_ft)(void*, size_t, const void*, size_t, int); |
86 | | typedef ZSTD_DStream* (*createDStream_ft)(void); |
87 | | typedef size_t (*initDStream_ft)(ZSTD_DStream*); |
88 | | typedef size_t (*freeDStream_ft)(ZSTD_DStream*); |
89 | | typedef size_t (*decompressStream_ft)(ZSTD_DStream*, ZSTD_outBuffer*, ZSTD_inBuffer*); |
90 | | typedef size_t (*decompress_ft)(void*, size_t, const void*, size_t); |
91 | | typedef unsigned (*isError_ft)(size_t); |
92 | | typedef const char* (*getErrorName_ft)(size_t); |
93 | | typedef size_t (*DStreamInSize_ft)(void); |
94 | | typedef size_t (*CStreamInSize_ft)(void); |
95 | | |
96 | | static createCStream_ft p_createCStream = NULL; |
97 | | static initCStream_ft p_initCStream = NULL; |
98 | | static freeCStream_ft p_freeCStream = NULL; |
99 | | static compressStream2_ft p_compressStream2 = NULL; |
100 | | static flushStream_ft p_flushStream = NULL; |
101 | | static endStream_ft p_endStream = NULL; |
102 | | static compress_ft p_compress = NULL; |
103 | | static createDStream_ft p_createDStream = NULL; |
104 | | static initDStream_ft p_initDStream = NULL; |
105 | | static freeDStream_ft p_freeDStream = NULL; |
106 | | static decompressStream_ft p_decompressStream = NULL; |
107 | | static decompress_ft p_decompress = NULL; |
108 | | static isError_ft p_isError = NULL; |
109 | | static getErrorName_ft p_getErrorName = NULL; |
110 | | static DStreamInSize_ft p_DStreamInSize = NULL; |
111 | | static CStreamInSize_ft p_CStreamInSize = NULL; |
112 | | |
113 | | static DSO *zstd_dso = NULL; |
114 | | |
115 | | # define ZSTD_createCStream p_createCStream |
116 | | # define ZSTD_initCStream p_initCStream |
117 | | # define ZSTD_freeCStream p_freeCStream |
118 | | # define ZSTD_compressStream2 p_compressStream2 |
119 | | # define ZSTD_flushStream p_flushStream |
120 | | # define ZSTD_endStream p_endStream |
121 | | # define ZSTD_compress p_compress |
122 | | # define ZSTD_createDStream p_createDStream |
123 | | # define ZSTD_initDStream p_initDStream |
124 | | # define ZSTD_freeDStream p_freeDStream |
125 | | # define ZSTD_decompressStream p_decompressStream |
126 | | # define ZSTD_decompress p_decompress |
127 | | # define ZSTD_isError p_isError |
128 | | # define ZSTD_getErrorName p_getErrorName |
129 | | # define ZSTD_DStreamInSize p_DStreamInSize |
130 | | # define ZSTD_CStreamInSize p_CStreamInSize |
131 | | |
132 | | # endif /* ifdef ZSTD_SHARED */ |
133 | | |
134 | | struct zstd_state { |
135 | | ZSTD_CStream *compressor; |
136 | | ZSTD_DStream *decompressor; |
137 | | }; |
138 | | |
139 | | static int zstd_stateful_init(COMP_CTX *ctx) |
140 | | { |
141 | | struct zstd_state *state = OPENSSL_zalloc(sizeof(*state)); |
142 | | |
143 | | if (state == NULL) |
144 | | return 0; |
145 | | |
146 | | # ifdef ZSTD_SHARED |
147 | | state->compressor = ZSTD_createCStream(); |
148 | | # else |
149 | | state->compressor = ZSTD_createCStream_advanced(zstd_mem_funcs); |
150 | | # endif |
151 | | if (state->compressor == NULL) |
152 | | goto err; |
153 | | ZSTD_initCStream(state->compressor, ZSTD_CLEVEL_DEFAULT); |
154 | | |
155 | | # ifdef ZSTD_SHARED |
156 | | state->decompressor = ZSTD_createDStream(); |
157 | | # else |
158 | | state->decompressor = ZSTD_createDStream_advanced(zstd_mem_funcs); |
159 | | # endif |
160 | | if (state->decompressor == NULL) |
161 | | goto err; |
162 | | ZSTD_initDStream(state->decompressor); |
163 | | |
164 | | ctx->data = state; |
165 | | return 1; |
166 | | err: |
167 | | ZSTD_freeCStream(state->compressor); |
168 | | ZSTD_freeDStream(state->decompressor); |
169 | | OPENSSL_free(state); |
170 | | return 0; |
171 | | } |
172 | | |
173 | | static void zstd_stateful_finish(COMP_CTX *ctx) |
174 | | { |
175 | | struct zstd_state *state = ctx->data; |
176 | | |
177 | | if (state != NULL) { |
178 | | ZSTD_freeCStream(state->compressor); |
179 | | ZSTD_freeDStream(state->decompressor); |
180 | | OPENSSL_free(state); |
181 | | ctx->data = NULL; |
182 | | } |
183 | | } |
184 | | |
185 | | static ossl_ssize_t zstd_stateful_compress_block(COMP_CTX *ctx, unsigned char *out, |
186 | | size_t olen, unsigned char *in, |
187 | | size_t ilen) |
188 | | { |
189 | | ZSTD_inBuffer inbuf; |
190 | | ZSTD_outBuffer outbuf; |
191 | | size_t ret; |
192 | | ossl_ssize_t fret; |
193 | | struct zstd_state *state = ctx->data; |
194 | | |
195 | | inbuf.src = in; |
196 | | inbuf.size = ilen; |
197 | | inbuf.pos = 0; |
198 | | outbuf.dst = out; |
199 | | outbuf.size = olen; |
200 | | outbuf.pos = 0; |
201 | | |
202 | | if (state == NULL) |
203 | | return -1; |
204 | | |
205 | | /* If input length is zero, end the stream/frame ? */ |
206 | | if (ilen == 0) { |
207 | | ret = ZSTD_endStream(state->compressor, &outbuf); |
208 | | if (ZSTD_isError(ret)) |
209 | | return -1; |
210 | | goto end; |
211 | | } |
212 | | |
213 | | /* |
214 | | * The finish API does not provide a final output buffer, |
215 | | * so each compress operation has to be ended, if all |
216 | | * the input data can't be accepted, or there is more output, |
217 | | * this has to be considered an error, since there is no more |
218 | | * output buffer space. |
219 | | */ |
220 | | do { |
221 | | ret = ZSTD_compressStream2(state->compressor, &outbuf, &inbuf, ZSTD_e_continue); |
222 | | if (ZSTD_isError(ret)) |
223 | | return -1; |
224 | | /* do I need to check for ret == 0 ? */ |
225 | | } while (inbuf.pos < inbuf.size); |
226 | | |
227 | | /* Did not consume all the data */ |
228 | | if (inbuf.pos < inbuf.size) |
229 | | return -1; |
230 | | |
231 | | ret = ZSTD_flushStream(state->compressor, &outbuf); |
232 | | if (ZSTD_isError(ret)) |
233 | | return -1; |
234 | | |
235 | | end: |
236 | | if (outbuf.pos > OSSL_SSIZE_MAX) |
237 | | return -1; |
238 | | fret = (ossl_ssize_t)outbuf.pos; |
239 | | if (fret < 0) |
240 | | return -1; |
241 | | return fret; |
242 | | } |
243 | | |
244 | | static ossl_ssize_t zstd_stateful_expand_block(COMP_CTX *ctx, unsigned char *out, |
245 | | size_t olen, unsigned char *in, |
246 | | size_t ilen) |
247 | | { |
248 | | ZSTD_inBuffer inbuf; |
249 | | ZSTD_outBuffer outbuf; |
250 | | size_t ret; |
251 | | ossl_ssize_t fret; |
252 | | struct zstd_state *state = ctx->data; |
253 | | |
254 | | inbuf.src = in; |
255 | | inbuf.size = ilen; |
256 | | inbuf.pos = 0; |
257 | | outbuf.dst = out; |
258 | | outbuf.size = olen; |
259 | | outbuf.pos = 0; |
260 | | |
261 | | if (state == NULL) |
262 | | return -1; |
263 | | |
264 | | if (ilen == 0) |
265 | | return 0; |
266 | | |
267 | | do { |
268 | | ret = ZSTD_decompressStream(state->decompressor, &outbuf, &inbuf); |
269 | | if (ZSTD_isError(ret)) |
270 | | return -1; |
271 | | /* If we completed a frame, and there's more data, try again */ |
272 | | } while (ret == 0 && inbuf.pos < inbuf.size); |
273 | | |
274 | | /* Did not consume all the data */ |
275 | | if (inbuf.pos < inbuf.size) |
276 | | return -1; |
277 | | |
278 | | if (outbuf.pos > OSSL_SSIZE_MAX) |
279 | | return -1; |
280 | | fret = (ossl_ssize_t)outbuf.pos; |
281 | | if (fret < 0) |
282 | | return -1; |
283 | | return fret; |
284 | | } |
285 | | |
286 | | |
287 | | static COMP_METHOD zstd_stateful_method = { |
288 | | NID_zstd, |
289 | | LN_zstd, |
290 | | zstd_stateful_init, |
291 | | zstd_stateful_finish, |
292 | | zstd_stateful_compress_block, |
293 | | zstd_stateful_expand_block |
294 | | }; |
295 | | |
296 | | static int zstd_oneshot_init(COMP_CTX *ctx) |
297 | | { |
298 | | return 1; |
299 | | } |
300 | | |
301 | | static void zstd_oneshot_finish(COMP_CTX *ctx) |
302 | | { |
303 | | } |
304 | | |
305 | | static ossl_ssize_t zstd_oneshot_compress_block(COMP_CTX *ctx, unsigned char *out, |
306 | | size_t olen, unsigned char *in, |
307 | | size_t ilen) |
308 | | { |
309 | | size_t out_size; |
310 | | ossl_ssize_t ret; |
311 | | |
312 | | if (ilen == 0) |
313 | | return 0; |
314 | | |
315 | | /* Note: uses STDLIB memory allocators */ |
316 | | out_size = ZSTD_compress(out, olen, in, ilen, ZSTD_CLEVEL_DEFAULT); |
317 | | if (ZSTD_isError(out_size)) |
318 | | return -1; |
319 | | |
320 | | if (out_size > OSSL_SSIZE_MAX) |
321 | | return -1; |
322 | | ret = (ossl_ssize_t)out_size; |
323 | | if (ret < 0) |
324 | | return -1; |
325 | | return ret; |
326 | | } |
327 | | |
328 | | static ossl_ssize_t zstd_oneshot_expand_block(COMP_CTX *ctx, unsigned char *out, |
329 | | size_t olen, unsigned char *in, |
330 | | size_t ilen) |
331 | | { |
332 | | size_t out_size; |
333 | | ossl_ssize_t ret; |
334 | | |
335 | | if (ilen == 0) |
336 | | return 0; |
337 | | |
338 | | /* Note: uses STDLIB memory allocators */ |
339 | | out_size = ZSTD_decompress(out, olen, in, ilen); |
340 | | if (ZSTD_isError(out_size)) |
341 | | return -1; |
342 | | |
343 | | if (out_size > OSSL_SSIZE_MAX) |
344 | | return -1; |
345 | | ret = (ossl_ssize_t)out_size; |
346 | | if (ret < 0) |
347 | | return -1; |
348 | | return ret; |
349 | | } |
350 | | |
351 | | static COMP_METHOD zstd_oneshot_method = { |
352 | | NID_zstd, |
353 | | LN_zstd, |
354 | | zstd_oneshot_init, |
355 | | zstd_oneshot_finish, |
356 | | zstd_oneshot_compress_block, |
357 | | zstd_oneshot_expand_block |
358 | | }; |
359 | | |
360 | | static CRYPTO_ONCE zstd_once = CRYPTO_ONCE_STATIC_INIT; |
361 | | DEFINE_RUN_ONCE_STATIC(ossl_comp_zstd_init) |
362 | | { |
363 | | # ifdef ZSTD_SHARED |
364 | | # if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) |
365 | | # define LIBZSTD "LIBZSTD" |
366 | | # else |
367 | | # define LIBZSTD "zstd" |
368 | | # endif |
369 | | |
370 | | zstd_dso = DSO_load(NULL, LIBZSTD, NULL, 0); |
371 | | if (zstd_dso != NULL) { |
372 | | p_createCStream = (createCStream_ft)DSO_bind_func(zstd_dso, "ZSTD_createCStream"); |
373 | | p_initCStream = (initCStream_ft)DSO_bind_func(zstd_dso, "ZSTD_initCStream"); |
374 | | p_freeCStream = (freeCStream_ft)DSO_bind_func(zstd_dso, "ZSTD_freeCStream"); |
375 | | p_compressStream2 = (compressStream2_ft)DSO_bind_func(zstd_dso, "ZSTD_compressStream2"); |
376 | | p_flushStream = (flushStream_ft)DSO_bind_func(zstd_dso, "ZSTD_flushStream"); |
377 | | p_endStream = (endStream_ft)DSO_bind_func(zstd_dso, "ZSTD_endStream"); |
378 | | p_compress = (compress_ft)DSO_bind_func(zstd_dso, "ZSTD_compress"); |
379 | | p_createDStream = (createDStream_ft)DSO_bind_func(zstd_dso, "ZSTD_createDStream"); |
380 | | p_initDStream = (initDStream_ft)DSO_bind_func(zstd_dso, "ZSTD_initDStream"); |
381 | | p_freeDStream = (freeDStream_ft)DSO_bind_func(zstd_dso, "ZSTD_freeDStream"); |
382 | | p_decompressStream = (decompressStream_ft)DSO_bind_func(zstd_dso, "ZSTD_decompressStream"); |
383 | | p_decompress = (decompress_ft)DSO_bind_func(zstd_dso, "ZSTD_decompress"); |
384 | | p_isError = (isError_ft)DSO_bind_func(zstd_dso, "ZSTD_isError"); |
385 | | p_getErrorName = (getErrorName_ft)DSO_bind_func(zstd_dso, "ZSTD_getErrorName"); |
386 | | p_DStreamInSize = (DStreamInSize_ft)DSO_bind_func(zstd_dso, "ZSTD_DStreamInSize"); |
387 | | p_CStreamInSize = (CStreamInSize_ft)DSO_bind_func(zstd_dso, "ZSTD_CStreamInSize"); |
388 | | } |
389 | | |
390 | | if (p_createCStream == NULL || p_initCStream == NULL || p_freeCStream == NULL |
391 | | || p_compressStream2 == NULL || p_flushStream == NULL || p_endStream == NULL |
392 | | || p_compress == NULL || p_createDStream == NULL || p_initDStream == NULL |
393 | | || p_freeDStream == NULL || p_decompressStream == NULL || p_decompress == NULL |
394 | | || p_isError == NULL || p_getErrorName == NULL || p_DStreamInSize == NULL |
395 | | || p_CStreamInSize == NULL) { |
396 | | ossl_comp_zstd_cleanup(); |
397 | | return 0; |
398 | | } |
399 | | # endif |
400 | | return 1; |
401 | | } |
402 | | #endif /* ifndef ZSTD / else */ |
403 | | |
404 | | COMP_METHOD *COMP_zstd(void) |
405 | 0 | { |
406 | 0 | COMP_METHOD *meth = NULL; |
407 | |
|
408 | | #ifndef OPENSSL_NO_ZSTD |
409 | | if (RUN_ONCE(&zstd_once, ossl_comp_zstd_init)) |
410 | | meth = &zstd_stateful_method; |
411 | | #endif |
412 | 0 | return meth; |
413 | 0 | } |
414 | | |
415 | | COMP_METHOD *COMP_zstd_oneshot(void) |
416 | 0 | { |
417 | 0 | COMP_METHOD *meth = NULL; |
418 | |
|
419 | | #ifndef OPENSSL_NO_ZSTD |
420 | | if (RUN_ONCE(&zstd_once, ossl_comp_zstd_init)) |
421 | | meth = &zstd_oneshot_method; |
422 | | #endif |
423 | 0 | return meth; |
424 | 0 | } |
425 | | |
426 | | /* Also called from OPENSSL_cleanup() */ |
427 | | void ossl_comp_zstd_cleanup(void) |
428 | 2 | { |
429 | | #ifdef ZSTD_SHARED |
430 | | DSO_free(zstd_dso); |
431 | | zstd_dso = NULL; |
432 | | p_createCStream = NULL; |
433 | | p_initCStream = NULL; |
434 | | p_freeCStream = NULL; |
435 | | p_compressStream2 = NULL; |
436 | | p_flushStream = NULL; |
437 | | p_endStream = NULL; |
438 | | p_compress = NULL; |
439 | | p_createDStream = NULL; |
440 | | p_initDStream = NULL; |
441 | | p_freeDStream = NULL; |
442 | | p_decompressStream = NULL; |
443 | | p_decompress = NULL; |
444 | | p_isError = NULL; |
445 | | p_getErrorName = NULL; |
446 | | p_DStreamInSize = NULL; |
447 | | p_CStreamInSize = NULL; |
448 | | #endif |
449 | 2 | } |
450 | | |
451 | | #ifndef OPENSSL_NO_ZSTD |
452 | | |
453 | | /* Zstd-based compression/decompression filter BIO */ |
454 | | |
455 | | typedef struct { |
456 | | struct { /* input structure */ |
457 | | ZSTD_DStream *state; |
458 | | ZSTD_inBuffer inbuf; /* has const src */ |
459 | | size_t bufsize; |
460 | | void* buffer; |
461 | | } decompress; |
462 | | struct { /* output structure */ |
463 | | ZSTD_CStream *state; |
464 | | ZSTD_outBuffer outbuf; |
465 | | size_t bufsize; |
466 | | size_t write_pos; |
467 | | } compress; |
468 | | } BIO_ZSTD_CTX; |
469 | | |
470 | | # define ZSTD_DEFAULT_BUFSIZE 1024 |
471 | | |
472 | | static int bio_zstd_new(BIO *bi); |
473 | | static int bio_zstd_free(BIO *bi); |
474 | | static int bio_zstd_read(BIO *b, char *out, int outl); |
475 | | static int bio_zstd_write(BIO *b, const char *in, int inl); |
476 | | static long bio_zstd_ctrl(BIO *b, int cmd, long num, void *ptr); |
477 | | static long bio_zstd_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp); |
478 | | |
479 | | static const BIO_METHOD bio_meth_zstd = { |
480 | | BIO_TYPE_COMP, |
481 | | "zstd", |
482 | | /* TODO: Convert to new style write function */ |
483 | | bwrite_conv, |
484 | | bio_zstd_write, |
485 | | /* TODO: Convert to new style read function */ |
486 | | bread_conv, |
487 | | bio_zstd_read, |
488 | | NULL, /* bio_zstd_puts, */ |
489 | | NULL, /* bio_zstd_gets, */ |
490 | | bio_zstd_ctrl, |
491 | | bio_zstd_new, |
492 | | bio_zstd_free, |
493 | | bio_zstd_callback_ctrl |
494 | | }; |
495 | | #endif |
496 | | |
497 | | const BIO_METHOD *BIO_f_zstd(void) |
498 | 0 | { |
499 | | #ifndef OPENSSL_NO_ZSTD |
500 | | if (RUN_ONCE(&zstd_once, ossl_comp_zstd_init)) |
501 | | return &bio_meth_zstd; |
502 | | #endif |
503 | 0 | return NULL; |
504 | 0 | } |
505 | | |
506 | | #ifndef OPENSSL_NO_ZSTD |
507 | | static int bio_zstd_new(BIO *bi) |
508 | | { |
509 | | BIO_ZSTD_CTX *ctx; |
510 | | |
511 | | # ifdef ZSTD_SHARED |
512 | | (void)COMP_zstd(); |
513 | | if (zstd_dso == NULL) { |
514 | | ERR_raise(ERR_LIB_COMP, COMP_R_ZSTD_NOT_SUPPORTED); |
515 | | return 0; |
516 | | } |
517 | | # endif |
518 | | ctx = OPENSSL_zalloc(sizeof(*ctx)); |
519 | | if (ctx == NULL) { |
520 | | ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); |
521 | | return 0; |
522 | | } |
523 | | |
524 | | # ifdef ZSTD_SHARED |
525 | | ctx->decompress.state = ZSTD_createDStream(); |
526 | | # else |
527 | | ctx->decompress.state = ZSTD_createDStream_advanced(zstd_mem_funcs); |
528 | | # endif |
529 | | if (ctx->decompress.state == NULL) |
530 | | goto err; |
531 | | ZSTD_initDStream(ctx->decompress.state); |
532 | | ctx->decompress.bufsize = ZSTD_DStreamInSize(); |
533 | | |
534 | | # ifdef ZSTD_SHARED |
535 | | ctx->compress.state = ZSTD_createCStream(); |
536 | | # else |
537 | | ctx->compress.state = ZSTD_createCStream_advanced(zstd_mem_funcs); |
538 | | # endif |
539 | | if (ctx->compress.state == NULL) |
540 | | goto err; |
541 | | ZSTD_initCStream(ctx->compress.state, ZSTD_CLEVEL_DEFAULT); |
542 | | ctx->compress.bufsize = ZSTD_CStreamInSize(); |
543 | | |
544 | | BIO_set_init(bi, 1); |
545 | | BIO_set_data(bi, ctx); |
546 | | |
547 | | return 1; |
548 | | err: |
549 | | ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); |
550 | | ZSTD_freeDStream(ctx->decompress.state); |
551 | | ZSTD_freeCStream(ctx->compress.state); |
552 | | OPENSSL_free(ctx); |
553 | | return 0; |
554 | | } |
555 | | |
556 | | static int bio_zstd_free(BIO *bi) |
557 | | { |
558 | | BIO_ZSTD_CTX *ctx; |
559 | | |
560 | | if (bi == NULL) |
561 | | return 0; |
562 | | |
563 | | ctx = BIO_get_data(bi); |
564 | | if (ctx != NULL) { |
565 | | ZSTD_freeDStream(ctx->decompress.state); |
566 | | OPENSSL_free(ctx->decompress.buffer); |
567 | | ZSTD_freeCStream(ctx->compress.state); |
568 | | OPENSSL_free(ctx->compress.outbuf.dst); |
569 | | OPENSSL_free(ctx); |
570 | | } |
571 | | BIO_set_data(bi, NULL); |
572 | | BIO_set_init(bi, 0); |
573 | | |
574 | | return 1; |
575 | | } |
576 | | |
577 | | static int bio_zstd_read(BIO *b, char *out, int outl) |
578 | | { |
579 | | BIO_ZSTD_CTX *ctx; |
580 | | size_t zret; |
581 | | int ret; |
582 | | ZSTD_outBuffer outBuf; |
583 | | BIO *next = BIO_next(b); |
584 | | |
585 | | if (out == NULL || outl <= 0) |
586 | | return 0; |
587 | | |
588 | | ctx = BIO_get_data(b); |
589 | | BIO_clear_retry_flags(b); |
590 | | if (ctx->decompress.buffer == NULL) { |
591 | | ctx->decompress.buffer = OPENSSL_malloc(ctx->decompress.bufsize); |
592 | | if (ctx->decompress.buffer == NULL) { |
593 | | ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); |
594 | | return 0; |
595 | | } |
596 | | ctx->decompress.inbuf.src = ctx->decompress.buffer; |
597 | | ctx->decompress.inbuf.size = 0; |
598 | | ctx->decompress.inbuf.pos = 0; |
599 | | } |
600 | | |
601 | | /* Copy output data directly to supplied buffer */ |
602 | | outBuf.dst = out; |
603 | | outBuf.size = (size_t)outl; |
604 | | outBuf.pos = 0; |
605 | | for (;;) { |
606 | | /* Decompress while data available */ |
607 | | do { |
608 | | zret = ZSTD_decompressStream(ctx->decompress.state, &outBuf, &ctx->decompress.inbuf); |
609 | | if (ZSTD_isError(zret)) { |
610 | | ERR_raise(ERR_LIB_COMP, COMP_R_ZSTD_DECOMPRESS_ERROR); |
611 | | ERR_add_error_data(1, ZSTD_getErrorName(zret)); |
612 | | return -1; |
613 | | } |
614 | | /* No more output space */ |
615 | | if (outBuf.pos == outBuf.size) |
616 | | return outBuf.pos; |
617 | | } while (ctx->decompress.inbuf.pos < ctx->decompress.inbuf.size); |
618 | | |
619 | | /* |
620 | | * No data in input buffer try to read some in, if an error then |
621 | | * return the total data read. |
622 | | */ |
623 | | ret = BIO_read(next, ctx->decompress.buffer, ctx->decompress.bufsize); |
624 | | if (ret <= 0) { |
625 | | BIO_copy_next_retry(b); |
626 | | if (ret < 0 && outBuf.pos == 0) |
627 | | return ret; |
628 | | return outBuf.pos; |
629 | | } |
630 | | ctx->decompress.inbuf.size = ret; |
631 | | ctx->decompress.inbuf.pos = 0; |
632 | | } |
633 | | } |
634 | | |
635 | | static int bio_zstd_write(BIO *b, const char *in, int inl) |
636 | | { |
637 | | BIO_ZSTD_CTX *ctx; |
638 | | size_t zret; |
639 | | ZSTD_inBuffer inBuf; |
640 | | int ret; |
641 | | int done = 0; |
642 | | BIO *next = BIO_next(b); |
643 | | |
644 | | if (in == NULL || inl <= 0) |
645 | | return 0; |
646 | | |
647 | | ctx = BIO_get_data(b); |
648 | | |
649 | | BIO_clear_retry_flags(b); |
650 | | if (ctx->compress.outbuf.dst == NULL) { |
651 | | ctx->compress.outbuf.dst = OPENSSL_malloc(ctx->compress.bufsize); |
652 | | if (ctx->compress.outbuf.dst == NULL) { |
653 | | ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); |
654 | | return 0; |
655 | | } |
656 | | ctx->compress.outbuf.size = ctx->compress.bufsize; |
657 | | ctx->compress.outbuf.pos = 0; |
658 | | ctx->compress.write_pos = 0; |
659 | | } |
660 | | /* Obtain input data directly from supplied buffer */ |
661 | | inBuf.src = in; |
662 | | inBuf.size = inl; |
663 | | inBuf.pos = 0; |
664 | | for (;;) { |
665 | | /* If data in output buffer write it first */ |
666 | | while (ctx->compress.write_pos < ctx->compress.outbuf.pos) { |
667 | | ret = BIO_write(next, (unsigned char*)ctx->compress.outbuf.dst + ctx->compress.write_pos, |
668 | | ctx->compress.outbuf.pos - ctx->compress.write_pos); |
669 | | if (ret <= 0) { |
670 | | BIO_copy_next_retry(b); |
671 | | if (ret < 0 && inBuf.pos == 0) |
672 | | return ret; |
673 | | return inBuf.pos; |
674 | | } |
675 | | ctx->compress.write_pos += ret; |
676 | | } |
677 | | |
678 | | /* Have we consumed all supplied data? */ |
679 | | if (done) |
680 | | return inBuf.pos; |
681 | | |
682 | | /* Reset buffer */ |
683 | | ctx->compress.outbuf.pos = 0; |
684 | | ctx->compress.outbuf.size = ctx->compress.bufsize; |
685 | | ctx->compress.write_pos = 0; |
686 | | /* Compress some more */ |
687 | | zret = ZSTD_compressStream2(ctx->compress.state, &ctx->compress.outbuf, &inBuf, ZSTD_e_end); |
688 | | if (ZSTD_isError(zret)) { |
689 | | ERR_raise(ERR_LIB_COMP, COMP_R_ZSTD_COMPRESS_ERROR); |
690 | | ERR_add_error_data(1, ZSTD_getErrorName(zret)); |
691 | | return 0; |
692 | | } else if (zret == 0) { |
693 | | done = 1; |
694 | | } |
695 | | } |
696 | | } |
697 | | |
698 | | static int bio_zstd_flush(BIO *b) |
699 | | { |
700 | | BIO_ZSTD_CTX *ctx; |
701 | | size_t zret; |
702 | | int ret; |
703 | | BIO *next = BIO_next(b); |
704 | | |
705 | | ctx = BIO_get_data(b); |
706 | | |
707 | | /* If no data written or already flush show success */ |
708 | | if (ctx->compress.outbuf.dst == NULL) |
709 | | return 1; |
710 | | |
711 | | BIO_clear_retry_flags(b); |
712 | | /* No more input data */ |
713 | | ctx->compress.outbuf.pos = 0; |
714 | | ctx->compress.outbuf.size = ctx->compress.bufsize; |
715 | | ctx->compress.write_pos = 0; |
716 | | for (;;) { |
717 | | /* If data in output buffer write it first */ |
718 | | while (ctx->compress.write_pos < ctx->compress.outbuf.pos) { |
719 | | ret = BIO_write(next, (unsigned char*)ctx->compress.outbuf.dst + ctx->compress.write_pos, |
720 | | ctx->compress.outbuf.pos - ctx->compress.write_pos); |
721 | | if (ret <= 0) { |
722 | | BIO_copy_next_retry(b); |
723 | | return ret; |
724 | | } |
725 | | ctx->compress.write_pos += ret; |
726 | | } |
727 | | |
728 | | /* Reset buffer */ |
729 | | ctx->compress.outbuf.pos = 0; |
730 | | ctx->compress.outbuf.size = ctx->compress.bufsize; |
731 | | ctx->compress.write_pos = 0; |
732 | | /* Compress some more */ |
733 | | zret = ZSTD_flushStream(ctx->compress.state, &ctx->compress.outbuf); |
734 | | if (ZSTD_isError(zret)) { |
735 | | ERR_raise(ERR_LIB_COMP, COMP_R_ZSTD_DECODE_ERROR); |
736 | | ERR_add_error_data(1, ZSTD_getErrorName(zret)); |
737 | | return 0; |
738 | | } |
739 | | if (zret == 0) |
740 | | return 1; |
741 | | } |
742 | | } |
743 | | |
744 | | static long bio_zstd_ctrl(BIO *b, int cmd, long num, void *ptr) |
745 | | { |
746 | | BIO_ZSTD_CTX *ctx; |
747 | | int ret = 0, *ip; |
748 | | size_t ibs, obs; |
749 | | unsigned char *tmp; |
750 | | BIO *next = BIO_next(b); |
751 | | |
752 | | if (next == NULL) |
753 | | return 0; |
754 | | ctx = BIO_get_data(b); |
755 | | switch (cmd) { |
756 | | |
757 | | case BIO_CTRL_RESET: |
758 | | ctx->compress.write_pos = 0; |
759 | | ctx->compress.bufsize = 0; |
760 | | ret = 1; |
761 | | break; |
762 | | |
763 | | case BIO_CTRL_FLUSH: |
764 | | ret = bio_zstd_flush(b); |
765 | | if (ret > 0) { |
766 | | ret = BIO_flush(next); |
767 | | BIO_copy_next_retry(b); |
768 | | } |
769 | | break; |
770 | | |
771 | | case BIO_C_SET_BUFF_SIZE: |
772 | | ibs = ctx->decompress.bufsize; |
773 | | obs = ctx->compress.bufsize; |
774 | | if (ptr != NULL) { |
775 | | ip = ptr; |
776 | | if (*ip == 0) |
777 | | ibs = (size_t)num; |
778 | | else |
779 | | obs = (size_t)num; |
780 | | } else { |
781 | | obs = ibs = (size_t)num; |
782 | | } |
783 | | |
784 | | if (ibs > 0 && ibs != ctx->decompress.bufsize) { |
785 | | if (ctx->decompress.buffer != NULL) { |
786 | | tmp = OPENSSL_realloc(ctx->decompress.buffer, ibs); |
787 | | if (tmp == NULL) |
788 | | return 0; |
789 | | if (ctx->decompress.inbuf.src == ctx->decompress.buffer) |
790 | | ctx->decompress.inbuf.src = tmp; |
791 | | ctx->decompress.buffer = tmp; |
792 | | } |
793 | | ctx->decompress.bufsize = ibs; |
794 | | } |
795 | | |
796 | | if (obs > 0 && obs != ctx->compress.bufsize) { |
797 | | if (ctx->compress.outbuf.dst != NULL) { |
798 | | tmp = OPENSSL_realloc(ctx->compress.outbuf.dst, obs); |
799 | | if (tmp == NULL) |
800 | | return 0; |
801 | | ctx->compress.outbuf.dst = tmp; |
802 | | } |
803 | | ctx->compress.bufsize = obs; |
804 | | } |
805 | | ret = 1; |
806 | | break; |
807 | | |
808 | | case BIO_C_DO_STATE_MACHINE: |
809 | | BIO_clear_retry_flags(b); |
810 | | ret = BIO_ctrl(next, cmd, num, ptr); |
811 | | BIO_copy_next_retry(b); |
812 | | break; |
813 | | |
814 | | case BIO_CTRL_WPENDING: |
815 | | if (ctx->compress.outbuf.pos < ctx->compress.outbuf.size) |
816 | | ret = 1; |
817 | | else |
818 | | ret = BIO_ctrl(next, cmd, num, ptr); |
819 | | break; |
820 | | |
821 | | case BIO_CTRL_PENDING: |
822 | | if (ctx->decompress.inbuf.pos < ctx->decompress.inbuf.size) |
823 | | ret = 1; |
824 | | else |
825 | | ret = BIO_ctrl(next, cmd, num, ptr); |
826 | | break; |
827 | | |
828 | | default: |
829 | | ret = BIO_ctrl(next, cmd, num, ptr); |
830 | | break; |
831 | | |
832 | | } |
833 | | |
834 | | return ret; |
835 | | } |
836 | | |
837 | | static long bio_zstd_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp) |
838 | | { |
839 | | BIO *next = BIO_next(b); |
840 | | if (next == NULL) |
841 | | return 0; |
842 | | return BIO_callback_ctrl(next, cmd, fp); |
843 | | } |
844 | | |
845 | | #endif |