xref: /openssl/crypto/comp/c_zstd.c (revision da1c088f)
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 */
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 
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 
zstd_oneshot_init(COMP_CTX * ctx)296 static int zstd_oneshot_init(COMP_CTX *ctx)
297 {
298     return 1;
299 }
300 
zstd_oneshot_finish(COMP_CTX * ctx)301 static void zstd_oneshot_finish(COMP_CTX *ctx)
302 {
303 }
304 
zstd_oneshot_compress_block(COMP_CTX * ctx,unsigned char * out,size_t olen,unsigned char * in,size_t ilen)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 
zstd_oneshot_expand_block(COMP_CTX * ctx,unsigned char * out,size_t olen,unsigned char * in,size_t ilen)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;
DEFINE_RUN_ONCE_STATIC(ossl_comp_zstd_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 
COMP_zstd(void)404 COMP_METHOD *COMP_zstd(void)
405 {
406     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     return meth;
413 }
414 
COMP_zstd_oneshot(void)415 COMP_METHOD *COMP_zstd_oneshot(void)
416 {
417     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     return meth;
424 }
425 
426 /* Also called from OPENSSL_cleanup() */
ossl_comp_zstd_cleanup(void)427 void ossl_comp_zstd_cleanup(void)
428 {
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 }
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 
BIO_f_zstd(void)497 const BIO_METHOD *BIO_f_zstd(void)
498 {
499 #ifndef OPENSSL_NO_ZSTD
500     if (RUN_ONCE(&zstd_once, ossl_comp_zstd_init))
501         return &bio_meth_zstd;
502 #endif
503     return NULL;
504 }
505 
506 #ifndef OPENSSL_NO_ZSTD
bio_zstd_new(BIO * bi)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 
bio_zstd_free(BIO * bi)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 
bio_zstd_read(BIO * b,char * out,int outl)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 
bio_zstd_write(BIO * b,const char * in,int inl)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 
bio_zstd_flush(BIO * b)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 
bio_zstd_ctrl(BIO * b,int cmd,long num,void * ptr)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 
bio_zstd_callback_ctrl(BIO * b,int cmd,BIO_info_cb * fp)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
846