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