/src/openssl/crypto/comp/c_zstd.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 1998-2025 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 | | zstd_dso = DSO_load(NULL, LIBZSTD, NULL, 0); |
361 | | if (zstd_dso != NULL) { |
362 | | p_createCStream = (createCStream_ft)DSO_bind_func(zstd_dso, "ZSTD_createCStream"); |
363 | | p_initCStream = (initCStream_ft)DSO_bind_func(zstd_dso, "ZSTD_initCStream"); |
364 | | p_freeCStream = (freeCStream_ft)DSO_bind_func(zstd_dso, "ZSTD_freeCStream"); |
365 | | p_compressStream2 = (compressStream2_ft)DSO_bind_func(zstd_dso, "ZSTD_compressStream2"); |
366 | | p_flushStream = (flushStream_ft)DSO_bind_func(zstd_dso, "ZSTD_flushStream"); |
367 | | p_endStream = (endStream_ft)DSO_bind_func(zstd_dso, "ZSTD_endStream"); |
368 | | p_compress = (compress_ft)DSO_bind_func(zstd_dso, "ZSTD_compress"); |
369 | | p_createDStream = (createDStream_ft)DSO_bind_func(zstd_dso, "ZSTD_createDStream"); |
370 | | p_initDStream = (initDStream_ft)DSO_bind_func(zstd_dso, "ZSTD_initDStream"); |
371 | | p_freeDStream = (freeDStream_ft)DSO_bind_func(zstd_dso, "ZSTD_freeDStream"); |
372 | | p_decompressStream = (decompressStream_ft)DSO_bind_func(zstd_dso, "ZSTD_decompressStream"); |
373 | | p_decompress = (decompress_ft)DSO_bind_func(zstd_dso, "ZSTD_decompress"); |
374 | | p_isError = (isError_ft)DSO_bind_func(zstd_dso, "ZSTD_isError"); |
375 | | p_getErrorName = (getErrorName_ft)DSO_bind_func(zstd_dso, "ZSTD_getErrorName"); |
376 | | p_DStreamInSize = (DStreamInSize_ft)DSO_bind_func(zstd_dso, "ZSTD_DStreamInSize"); |
377 | | p_CStreamInSize = (CStreamInSize_ft)DSO_bind_func(zstd_dso, "ZSTD_CStreamInSize"); |
378 | | } |
379 | | |
380 | | if (p_createCStream == NULL || p_initCStream == NULL || p_freeCStream == NULL |
381 | | || p_compressStream2 == NULL || p_flushStream == NULL || p_endStream == NULL |
382 | | || p_compress == NULL || p_createDStream == NULL || p_initDStream == NULL |
383 | | || p_freeDStream == NULL || p_decompressStream == NULL || p_decompress == NULL |
384 | | || p_isError == NULL || p_getErrorName == NULL || p_DStreamInSize == NULL |
385 | | || p_CStreamInSize == NULL) { |
386 | | ossl_comp_zstd_cleanup(); |
387 | | return 0; |
388 | | } |
389 | | #endif |
390 | | return 1; |
391 | | } |
392 | | #endif /* ifndef ZSTD / else */ |
393 | | |
394 | | COMP_METHOD *COMP_zstd(void) |
395 | 0 | { |
396 | 0 | COMP_METHOD *meth = NULL; |
397 | |
|
398 | | #ifndef OPENSSL_NO_ZSTD |
399 | | if (RUN_ONCE(&zstd_once, ossl_comp_zstd_init)) |
400 | | meth = &zstd_stateful_method; |
401 | | #endif |
402 | 0 | return meth; |
403 | 0 | } |
404 | | |
405 | | COMP_METHOD *COMP_zstd_oneshot(void) |
406 | 0 | { |
407 | 0 | COMP_METHOD *meth = NULL; |
408 | |
|
409 | | #ifndef OPENSSL_NO_ZSTD |
410 | | if (RUN_ONCE(&zstd_once, ossl_comp_zstd_init)) |
411 | | meth = &zstd_oneshot_method; |
412 | | #endif |
413 | 0 | return meth; |
414 | 0 | } |
415 | | |
416 | | /* Also called from OPENSSL_cleanup() */ |
417 | | void ossl_comp_zstd_cleanup(void) |
418 | 0 | { |
419 | | #ifdef ZSTD_SHARED |
420 | | DSO_free(zstd_dso); |
421 | | zstd_dso = NULL; |
422 | | p_createCStream = NULL; |
423 | | p_initCStream = NULL; |
424 | | p_freeCStream = NULL; |
425 | | p_compressStream2 = NULL; |
426 | | p_flushStream = NULL; |
427 | | p_endStream = NULL; |
428 | | p_compress = NULL; |
429 | | p_createDStream = NULL; |
430 | | p_initDStream = NULL; |
431 | | p_freeDStream = NULL; |
432 | | p_decompressStream = NULL; |
433 | | p_decompress = NULL; |
434 | | p_isError = NULL; |
435 | | p_getErrorName = NULL; |
436 | | p_DStreamInSize = NULL; |
437 | | p_CStreamInSize = NULL; |
438 | | #endif |
439 | 0 | } |
440 | | |
441 | | #ifndef OPENSSL_NO_ZSTD |
442 | | |
443 | | /* Zstd-based compression/decompression filter BIO */ |
444 | | |
445 | | typedef struct { |
446 | | struct { /* input structure */ |
447 | | ZSTD_DStream *state; |
448 | | ZSTD_inBuffer inbuf; /* has const src */ |
449 | | size_t bufsize; |
450 | | void *buffer; |
451 | | } decompress; |
452 | | struct { /* output structure */ |
453 | | ZSTD_CStream *state; |
454 | | ZSTD_outBuffer outbuf; |
455 | | size_t bufsize; |
456 | | size_t write_pos; |
457 | | } compress; |
458 | | } BIO_ZSTD_CTX; |
459 | | |
460 | | #define ZSTD_DEFAULT_BUFSIZE 1024 |
461 | | |
462 | | static int bio_zstd_new(BIO *bi); |
463 | | static int bio_zstd_free(BIO *bi); |
464 | | static int bio_zstd_read(BIO *b, char *out, int outl); |
465 | | static int bio_zstd_write(BIO *b, const char *in, int inl); |
466 | | static long bio_zstd_ctrl(BIO *b, int cmd, long num, void *ptr); |
467 | | static long bio_zstd_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp); |
468 | | |
469 | | static const BIO_METHOD bio_meth_zstd = { |
470 | | BIO_TYPE_COMP, |
471 | | "zstd", |
472 | | /* TODO: Convert to new style write function */ |
473 | | bwrite_conv, |
474 | | bio_zstd_write, |
475 | | /* TODO: Convert to new style read function */ |
476 | | bread_conv, |
477 | | bio_zstd_read, |
478 | | NULL, /* bio_zstd_puts, */ |
479 | | NULL, /* bio_zstd_gets, */ |
480 | | bio_zstd_ctrl, |
481 | | bio_zstd_new, |
482 | | bio_zstd_free, |
483 | | bio_zstd_callback_ctrl |
484 | | }; |
485 | | #endif |
486 | | |
487 | | const BIO_METHOD *BIO_f_zstd(void) |
488 | 0 | { |
489 | | #ifndef OPENSSL_NO_ZSTD |
490 | | if (RUN_ONCE(&zstd_once, ossl_comp_zstd_init)) |
491 | | return &bio_meth_zstd; |
492 | | #endif |
493 | | return NULL; |
494 | 0 | } |
495 | | |
496 | | #ifndef OPENSSL_NO_ZSTD |
497 | | static int bio_zstd_new(BIO *bi) |
498 | | { |
499 | | BIO_ZSTD_CTX *ctx; |
500 | | |
501 | | #ifdef ZSTD_SHARED |
502 | | (void)COMP_zstd(); |
503 | | if (zstd_dso == NULL) { |
504 | | ERR_raise(ERR_LIB_COMP, COMP_R_ZSTD_NOT_SUPPORTED); |
505 | | return 0; |
506 | | } |
507 | | #endif |
508 | | ctx = OPENSSL_zalloc(sizeof(*ctx)); |
509 | | if (ctx == NULL) { |
510 | | ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); |
511 | | return 0; |
512 | | } |
513 | | |
514 | | #ifdef ZSTD_SHARED |
515 | | ctx->decompress.state = ZSTD_createDStream(); |
516 | | #else |
517 | | ctx->decompress.state = ZSTD_createDStream_advanced(zstd_mem_funcs); |
518 | | #endif |
519 | | if (ctx->decompress.state == NULL) |
520 | | goto err; |
521 | | ZSTD_initDStream(ctx->decompress.state); |
522 | | ctx->decompress.bufsize = ZSTD_DStreamInSize(); |
523 | | |
524 | | #ifdef ZSTD_SHARED |
525 | | ctx->compress.state = ZSTD_createCStream(); |
526 | | #else |
527 | | ctx->compress.state = ZSTD_createCStream_advanced(zstd_mem_funcs); |
528 | | #endif |
529 | | if (ctx->compress.state == NULL) |
530 | | goto err; |
531 | | ZSTD_initCStream(ctx->compress.state, ZSTD_CLEVEL_DEFAULT); |
532 | | ctx->compress.bufsize = ZSTD_CStreamInSize(); |
533 | | |
534 | | BIO_set_init(bi, 1); |
535 | | BIO_set_data(bi, ctx); |
536 | | |
537 | | return 1; |
538 | | err: |
539 | | ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); |
540 | | ZSTD_freeDStream(ctx->decompress.state); |
541 | | ZSTD_freeCStream(ctx->compress.state); |
542 | | OPENSSL_free(ctx); |
543 | | return 0; |
544 | | } |
545 | | |
546 | | static int bio_zstd_free(BIO *bi) |
547 | | { |
548 | | BIO_ZSTD_CTX *ctx; |
549 | | |
550 | | if (bi == NULL) |
551 | | return 0; |
552 | | |
553 | | ctx = BIO_get_data(bi); |
554 | | if (ctx != NULL) { |
555 | | ZSTD_freeDStream(ctx->decompress.state); |
556 | | OPENSSL_free(ctx->decompress.buffer); |
557 | | ZSTD_freeCStream(ctx->compress.state); |
558 | | OPENSSL_free(ctx->compress.outbuf.dst); |
559 | | OPENSSL_free(ctx); |
560 | | } |
561 | | BIO_set_data(bi, NULL); |
562 | | BIO_set_init(bi, 0); |
563 | | |
564 | | return 1; |
565 | | } |
566 | | |
567 | | static int bio_zstd_read(BIO *b, char *out, int outl) |
568 | | { |
569 | | BIO_ZSTD_CTX *ctx; |
570 | | size_t zret; |
571 | | int ret; |
572 | | ZSTD_outBuffer outBuf; |
573 | | BIO *next = BIO_next(b); |
574 | | |
575 | | if (out == NULL) { |
576 | | ERR_raise(ERR_LIB_COMP, ERR_R_PASSED_NULL_PARAMETER); |
577 | | return -1; |
578 | | } |
579 | | if (outl <= 0) |
580 | | return 0; |
581 | | |
582 | | ctx = BIO_get_data(b); |
583 | | BIO_clear_retry_flags(b); |
584 | | if (ctx->decompress.buffer == NULL) { |
585 | | ctx->decompress.buffer = OPENSSL_malloc(ctx->decompress.bufsize); |
586 | | if (ctx->decompress.buffer == NULL) { |
587 | | ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); |
588 | | return -1; |
589 | | } |
590 | | ctx->decompress.inbuf.src = ctx->decompress.buffer; |
591 | | ctx->decompress.inbuf.size = 0; |
592 | | ctx->decompress.inbuf.pos = 0; |
593 | | } |
594 | | |
595 | | /* Copy output data directly to supplied buffer */ |
596 | | outBuf.dst = out; |
597 | | outBuf.size = (size_t)outl; |
598 | | outBuf.pos = 0; |
599 | | for (;;) { |
600 | | /* Decompress while data available */ |
601 | | do { |
602 | | zret = ZSTD_decompressStream(ctx->decompress.state, &outBuf, &ctx->decompress.inbuf); |
603 | | if (ZSTD_isError(zret)) { |
604 | | ERR_raise(ERR_LIB_COMP, COMP_R_ZSTD_DECOMPRESS_ERROR); |
605 | | ERR_add_error_data(1, ZSTD_getErrorName(zret)); |
606 | | return -1; |
607 | | } |
608 | | /* No more output space */ |
609 | | if (outBuf.pos == outBuf.size) |
610 | | return (int)outBuf.pos; |
611 | | } while (ctx->decompress.inbuf.pos < ctx->decompress.inbuf.size); |
612 | | |
613 | | /* |
614 | | * No data in input buffer try to read some in, if an error then |
615 | | * return the total data read. |
616 | | */ |
617 | | ret = BIO_read(next, ctx->decompress.buffer, (int)ctx->decompress.bufsize); |
618 | | if (ret <= 0) { |
619 | | BIO_copy_next_retry(b); |
620 | | if (ret < 0 && outBuf.pos == 0) |
621 | | return ret; |
622 | | return (int)outBuf.pos; |
623 | | } |
624 | | ctx->decompress.inbuf.size = ret; |
625 | | ctx->decompress.inbuf.pos = 0; |
626 | | } |
627 | | } |
628 | | |
629 | | static int bio_zstd_write(BIO *b, const char *in, int inl) |
630 | | { |
631 | | BIO_ZSTD_CTX *ctx; |
632 | | size_t zret; |
633 | | ZSTD_inBuffer inBuf; |
634 | | int ret; |
635 | | int done = 0; |
636 | | BIO *next = BIO_next(b); |
637 | | |
638 | | if (in == NULL || inl <= 0) |
639 | | return 0; |
640 | | |
641 | | ctx = BIO_get_data(b); |
642 | | |
643 | | BIO_clear_retry_flags(b); |
644 | | if (ctx->compress.outbuf.dst == NULL) { |
645 | | ctx->compress.outbuf.dst = OPENSSL_malloc(ctx->compress.bufsize); |
646 | | if (ctx->compress.outbuf.dst == NULL) { |
647 | | ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); |
648 | | return 0; |
649 | | } |
650 | | ctx->compress.outbuf.size = ctx->compress.bufsize; |
651 | | ctx->compress.outbuf.pos = 0; |
652 | | ctx->compress.write_pos = 0; |
653 | | } |
654 | | /* Obtain input data directly from supplied buffer */ |
655 | | inBuf.src = in; |
656 | | inBuf.size = inl; |
657 | | inBuf.pos = 0; |
658 | | for (;;) { |
659 | | /* If data in output buffer write it first */ |
660 | | while (ctx->compress.write_pos < ctx->compress.outbuf.pos) { |
661 | | ret = BIO_write(next, (unsigned char *)ctx->compress.outbuf.dst + ctx->compress.write_pos, |
662 | | (int)(ctx->compress.outbuf.pos - ctx->compress.write_pos)); |
663 | | if (ret <= 0) { |
664 | | BIO_copy_next_retry(b); |
665 | | if (ret < 0 && inBuf.pos == 0) |
666 | | return ret; |
667 | | return (int)inBuf.pos; |
668 | | } |
669 | | ctx->compress.write_pos += ret; |
670 | | } |
671 | | |
672 | | /* Have we consumed all supplied data? */ |
673 | | if (done) |
674 | | return (int)inBuf.pos; |
675 | | |
676 | | /* Reset buffer */ |
677 | | ctx->compress.outbuf.pos = 0; |
678 | | ctx->compress.outbuf.size = ctx->compress.bufsize; |
679 | | ctx->compress.write_pos = 0; |
680 | | /* Compress some more */ |
681 | | zret = ZSTD_compressStream2(ctx->compress.state, &ctx->compress.outbuf, &inBuf, ZSTD_e_end); |
682 | | if (ZSTD_isError(zret)) { |
683 | | ERR_raise(ERR_LIB_COMP, COMP_R_ZSTD_COMPRESS_ERROR); |
684 | | ERR_add_error_data(1, ZSTD_getErrorName(zret)); |
685 | | return 0; |
686 | | } else if (zret == 0) { |
687 | | done = 1; |
688 | | } |
689 | | } |
690 | | } |
691 | | |
692 | | static int bio_zstd_flush(BIO *b) |
693 | | { |
694 | | BIO_ZSTD_CTX *ctx; |
695 | | size_t zret; |
696 | | int ret; |
697 | | BIO *next = BIO_next(b); |
698 | | |
699 | | ctx = BIO_get_data(b); |
700 | | |
701 | | /* If no data written or already flush show success */ |
702 | | if (ctx->compress.outbuf.dst == NULL) |
703 | | return 1; |
704 | | |
705 | | BIO_clear_retry_flags(b); |
706 | | /* No more input data */ |
707 | | ctx->compress.outbuf.pos = 0; |
708 | | ctx->compress.outbuf.size = ctx->compress.bufsize; |
709 | | ctx->compress.write_pos = 0; |
710 | | for (;;) { |
711 | | /* If data in output buffer write it first */ |
712 | | while (ctx->compress.write_pos < ctx->compress.outbuf.pos) { |
713 | | ret = BIO_write(next, (unsigned char *)ctx->compress.outbuf.dst + ctx->compress.write_pos, |
714 | | (int)(ctx->compress.outbuf.pos - ctx->compress.write_pos)); |
715 | | if (ret <= 0) { |
716 | | BIO_copy_next_retry(b); |
717 | | return ret; |
718 | | } |
719 | | ctx->compress.write_pos += ret; |
720 | | } |
721 | | |
722 | | /* Reset buffer */ |
723 | | ctx->compress.outbuf.pos = 0; |
724 | | ctx->compress.outbuf.size = ctx->compress.bufsize; |
725 | | ctx->compress.write_pos = 0; |
726 | | /* Compress some more */ |
727 | | zret = ZSTD_flushStream(ctx->compress.state, &ctx->compress.outbuf); |
728 | | if (ZSTD_isError(zret)) { |
729 | | ERR_raise(ERR_LIB_COMP, COMP_R_ZSTD_COMPRESS_ERROR); |
730 | | ERR_add_error_data(1, ZSTD_getErrorName(zret)); |
731 | | return 0; |
732 | | } |
733 | | if (zret == 0) |
734 | | return 1; |
735 | | } |
736 | | } |
737 | | |
738 | | static long bio_zstd_ctrl(BIO *b, int cmd, long num, void *ptr) |
739 | | { |
740 | | BIO_ZSTD_CTX *ctx; |
741 | | int ret = 0, *ip; |
742 | | size_t ibs, obs; |
743 | | unsigned char *tmp; |
744 | | BIO *next = BIO_next(b); |
745 | | |
746 | | if (next == NULL) |
747 | | return 0; |
748 | | ctx = BIO_get_data(b); |
749 | | switch (cmd) { |
750 | | |
751 | | case BIO_CTRL_RESET: |
752 | | /* reset decompressor */ |
753 | | ctx->decompress.inbuf.size = 0; |
754 | | ctx->decompress.inbuf.pos = 0; |
755 | | if (ctx->decompress.state != NULL) |
756 | | ZSTD_initDStream(ctx->decompress.state); |
757 | | |
758 | | /* reset compressor */ |
759 | | ctx->compress.write_pos = 0; |
760 | | ctx->compress.outbuf.pos = 0; |
761 | | if (ctx->compress.state != NULL) |
762 | | ZSTD_initCStream(ctx->compress.state, ZSTD_CLEVEL_DEFAULT); |
763 | | |
764 | | /* keep existing bufsize, do not set it to 0 */ |
765 | | ret = 1; |
766 | | break; |
767 | | |
768 | | case BIO_CTRL_FLUSH: |
769 | | ret = bio_zstd_flush(b); |
770 | | if (ret > 0) { |
771 | | ret = BIO_flush(next); |
772 | | BIO_copy_next_retry(b); |
773 | | } |
774 | | break; |
775 | | |
776 | | case BIO_C_SET_BUFF_SIZE: |
777 | | ibs = ctx->decompress.bufsize; |
778 | | obs = ctx->compress.bufsize; |
779 | | if (ptr != NULL) { |
780 | | ip = ptr; |
781 | | if (*ip == 0) |
782 | | ibs = (size_t)num; |
783 | | else |
784 | | obs = (size_t)num; |
785 | | } else { |
786 | | obs = ibs = (size_t)num; |
787 | | } |
788 | | |
789 | | if (ibs > 0 && ibs != ctx->decompress.bufsize) { |
790 | | if (ctx->decompress.buffer != NULL) { |
791 | | tmp = OPENSSL_realloc(ctx->decompress.buffer, ibs); |
792 | | if (tmp == NULL) |
793 | | return 0; |
794 | | if (ctx->decompress.inbuf.src == ctx->decompress.buffer) |
795 | | ctx->decompress.inbuf.src = tmp; |
796 | | ctx->decompress.buffer = tmp; |
797 | | } |
798 | | ctx->decompress.bufsize = ibs; |
799 | | } |
800 | | |
801 | | if (obs > 0 && obs != ctx->compress.bufsize) { |
802 | | if (ctx->compress.outbuf.dst != NULL) { |
803 | | tmp = OPENSSL_realloc(ctx->compress.outbuf.dst, obs); |
804 | | if (tmp == NULL) |
805 | | return 0; |
806 | | ctx->compress.outbuf.dst = tmp; |
807 | | } |
808 | | ctx->compress.bufsize = obs; |
809 | | } |
810 | | ret = 1; |
811 | | break; |
812 | | |
813 | | case BIO_C_DO_STATE_MACHINE: |
814 | | BIO_clear_retry_flags(b); |
815 | | ret = BIO_ctrl(next, cmd, num, ptr); |
816 | | BIO_copy_next_retry(b); |
817 | | break; |
818 | | |
819 | | case BIO_CTRL_WPENDING: |
820 | | if (ctx->compress.outbuf.pos < ctx->compress.outbuf.size) |
821 | | ret = 1; |
822 | | else |
823 | | ret = BIO_ctrl(next, cmd, num, ptr); |
824 | | break; |
825 | | |
826 | | case BIO_CTRL_PENDING: |
827 | | if (ctx->decompress.inbuf.pos < ctx->decompress.inbuf.size) |
828 | | ret = 1; |
829 | | else |
830 | | ret = BIO_ctrl(next, cmd, num, ptr); |
831 | | break; |
832 | | |
833 | | default: |
834 | | ret = BIO_ctrl(next, cmd, num, ptr); |
835 | | break; |
836 | | } |
837 | | |
838 | | return ret; |
839 | | } |
840 | | |
841 | | static long bio_zstd_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp) |
842 | | { |
843 | | BIO *next = BIO_next(b); |
844 | | if (next == NULL) |
845 | | return 0; |
846 | | return BIO_callback_ctrl(next, cmd, fp); |
847 | | } |
848 | | |
849 | | #endif |