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