xref: /openssl/crypto/comp/c_brotli.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 brotli compression library from https://github.com/google/brotli
10  */
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <openssl/objects.h>
16 #include "internal/comp.h"
17 #include <openssl/err.h>
18 #include "crypto/cryptlib.h"
19 #include "internal/bio.h"
20 #include "internal/thread_once.h"
21 #include "comp_local.h"
22 
23 COMP_METHOD *COMP_brotli(void);
24 
25 #ifdef OPENSSL_NO_BROTLI
26 # undef BROTLI_SHARED
27 #else
28 
29 # include <brotli/decode.h>
30 # include <brotli/encode.h>
31 
32 /* memory allocations functions for brotli initialisation */
brotli_alloc(void * opaque,size_t size)33 static void *brotli_alloc(void *opaque, size_t size)
34 {
35     return OPENSSL_zalloc(size);
36 }
37 
brotli_free(void * opaque,void * address)38 static void brotli_free(void *opaque, void *address)
39 {
40     OPENSSL_free(address);
41 }
42 
43 /*
44  * When OpenSSL is built on Windows, we do not want to require that
45  * the BROTLI.DLL be available in order for the OpenSSL DLLs to
46  * work.  Therefore, all BROTLI routines are loaded at run time
47  * and we do not link to a .LIB file when BROTLI_SHARED is set.
48  */
49 # if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32)
50 #  include <windows.h>
51 # endif
52 
53 # ifdef BROTLI_SHARED
54 #  include "internal/dso.h"
55 
56 /* Function pointers */
57 typedef BrotliEncoderState *(*encode_init_ft)(brotli_alloc_func, brotli_free_func, void *);
58 typedef BROTLI_BOOL (*encode_stream_ft)(BrotliEncoderState *, BrotliEncoderOperation, size_t *, const uint8_t **, size_t *, uint8_t **, size_t *);
59 typedef BROTLI_BOOL (*encode_has_more_ft)(BrotliEncoderState *);
60 typedef void (*encode_end_ft)(BrotliEncoderState *);
61 typedef BROTLI_BOOL (*encode_oneshot_ft)(int, int, BrotliEncoderMode, size_t, const uint8_t in[], size_t *, uint8_t out[]);
62 
63 typedef BrotliDecoderState *(*decode_init_ft)(brotli_alloc_func, brotli_free_func, void *);
64 typedef BROTLI_BOOL (*decode_stream_ft)(BrotliDecoderState *, size_t *, const uint8_t **, size_t *, uint8_t **, size_t *);
65 typedef BROTLI_BOOL (*decode_has_more_ft)(BrotliDecoderState *);
66 typedef void (*decode_end_ft)(BrotliDecoderState *);
67 typedef BrotliDecoderErrorCode (*decode_error_ft)(BrotliDecoderState *);
68 typedef const char *(*decode_error_string_ft)(BrotliDecoderErrorCode);
69 typedef BROTLI_BOOL (*decode_is_finished_ft)(BrotliDecoderState *);
70 typedef BrotliDecoderResult (*decode_oneshot_ft)(size_t, const uint8_t in[], size_t *, uint8_t out[]);
71 
72 static encode_init_ft p_encode_init = NULL;
73 static encode_stream_ft p_encode_stream = NULL;
74 static encode_has_more_ft p_encode_has_more = NULL;
75 static encode_end_ft p_encode_end = NULL;
76 static encode_oneshot_ft p_encode_oneshot = NULL;
77 
78 static decode_init_ft p_decode_init = NULL;
79 static decode_stream_ft p_decode_stream = NULL;
80 static decode_has_more_ft p_decode_has_more = NULL;
81 static decode_end_ft p_decode_end = NULL;
82 static decode_error_ft p_decode_error = NULL;
83 static decode_error_string_ft p_decode_error_string = NULL;
84 static decode_is_finished_ft p_decode_is_finished = NULL;
85 static decode_oneshot_ft p_decode_oneshot = NULL;
86 
87 static DSO *brotli_encode_dso = NULL;
88 static DSO *brotli_decode_dso = NULL;
89 
90 #  define BrotliEncoderCreateInstance p_encode_init
91 #  define BrotliEncoderCompressStream p_encode_stream
92 #  define BrotliEncoderHasMoreOutput p_encode_has_more
93 #  define BrotliEncoderDestroyInstance p_encode_end
94 #  define BrotliEncoderCompress p_encode_oneshot
95 
96 #  define BrotliDecoderCreateInstance p_decode_init
97 #  define BrotliDecoderDecompressStream p_decode_stream
98 #  define BrotliDecoderHasMoreOutput p_decode_has_more
99 #  define BrotliDecoderDestroyInstance p_decode_end
100 #  define BrotliDecoderGetErrorCode p_decode_error
101 #  define BrotliDecoderErrorString p_decode_error_string
102 #  define BrotliDecoderIsFinished p_decode_is_finished
103 #  define BrotliDecoderDecompress p_decode_oneshot
104 
105 # endif /* ifdef BROTLI_SHARED */
106 
107 
108 struct brotli_state {
109     BrotliEncoderState *encoder;
110     BrotliDecoderState *decoder;
111 };
112 
brotli_stateful_init(COMP_CTX * ctx)113 static int brotli_stateful_init(COMP_CTX *ctx)
114 {
115     struct brotli_state *state = OPENSSL_zalloc(sizeof(*state));
116 
117     if (state == NULL)
118         return 0;
119 
120     state->encoder = BrotliEncoderCreateInstance(brotli_alloc, brotli_free, NULL);
121     if (state->encoder == NULL)
122         goto err;
123 
124     state->decoder = BrotliDecoderCreateInstance(brotli_alloc, brotli_free, NULL);
125     if (state->decoder == NULL)
126         goto err;
127 
128     ctx->data = state;
129     return 1;
130  err:
131     BrotliDecoderDestroyInstance(state->decoder);
132     BrotliEncoderDestroyInstance(state->encoder);
133     OPENSSL_free(state);
134     return 0;
135 }
136 
brotli_stateful_finish(COMP_CTX * ctx)137 static void brotli_stateful_finish(COMP_CTX *ctx)
138 {
139     struct brotli_state *state = ctx->data;
140 
141     if (state != NULL) {
142         BrotliDecoderDestroyInstance(state->decoder);
143         BrotliEncoderDestroyInstance(state->encoder);
144         OPENSSL_free(state);
145         ctx->data = NULL;
146     }
147 }
148 
brotli_stateful_compress_block(COMP_CTX * ctx,unsigned char * out,size_t olen,unsigned char * in,size_t ilen)149 static ossl_ssize_t brotli_stateful_compress_block(COMP_CTX *ctx, unsigned char *out,
150                                                    size_t olen, unsigned char *in,
151                                                    size_t ilen)
152 {
153     BROTLI_BOOL done;
154     struct brotli_state *state = ctx->data;
155     size_t in_avail = ilen;
156     size_t out_avail = olen;
157 
158     if (state == NULL || olen > OSSL_SSIZE_MAX)
159         return -1;
160 
161     if (ilen == 0)
162         return 0;
163 
164     /*
165      * The finish API does not provide a final output buffer,
166      * so each compress operation has to be flushed, if all
167      * the input data can't be accepted, or there is more output,
168      * this has to be considered an error, since there is no more
169      * output buffer space
170      */
171     done = BrotliEncoderCompressStream(state->encoder, BROTLI_OPERATION_FLUSH,
172                                        &in_avail, (const uint8_t**)&in,
173                                        &out_avail, &out, NULL);
174     if (done == BROTLI_FALSE
175             || in_avail != 0
176             || BrotliEncoderHasMoreOutput(state->encoder))
177         return -1;
178 
179     if (out_avail > olen)
180         return -1;
181     return (ossl_ssize_t)(olen - out_avail);
182 }
183 
brotli_stateful_expand_block(COMP_CTX * ctx,unsigned char * out,size_t olen,unsigned char * in,size_t ilen)184 static ossl_ssize_t brotli_stateful_expand_block(COMP_CTX *ctx, unsigned char *out,
185                                                  size_t olen, unsigned char *in,
186                                                  size_t ilen)
187 {
188     BrotliDecoderResult result;
189     struct brotli_state *state = ctx->data;
190     size_t in_avail = ilen;
191     size_t out_avail = olen;
192 
193     if (state == NULL || olen > OSSL_SSIZE_MAX)
194         return -1;
195 
196     if (ilen == 0)
197         return 0;
198 
199     result = BrotliDecoderDecompressStream(state->decoder, &in_avail,
200                                            (const uint8_t**)&in, &out_avail,
201                                            &out, NULL);
202     if (result == BROTLI_DECODER_RESULT_ERROR
203             || in_avail != 0
204             || BrotliDecoderHasMoreOutput(state->decoder))
205         return -1;
206 
207     if (out_avail > olen)
208         return -1;
209     return (ossl_ssize_t)(olen - out_avail);
210 }
211 
212 static COMP_METHOD brotli_stateful_method = {
213     NID_brotli,
214     LN_brotli,
215     brotli_stateful_init,
216     brotli_stateful_finish,
217     brotli_stateful_compress_block,
218     brotli_stateful_expand_block
219 };
220 
brotli_oneshot_init(COMP_CTX * ctx)221 static int brotli_oneshot_init(COMP_CTX *ctx)
222 {
223     return 1;
224 }
225 
brotli_oneshot_finish(COMP_CTX * ctx)226 static void brotli_oneshot_finish(COMP_CTX *ctx)
227 {
228 }
229 
brotli_oneshot_compress_block(COMP_CTX * ctx,unsigned char * out,size_t olen,unsigned char * in,size_t ilen)230 static ossl_ssize_t brotli_oneshot_compress_block(COMP_CTX *ctx, unsigned char *out,
231                                                   size_t olen, unsigned char *in,
232                                                   size_t ilen)
233 {
234     size_t out_size = olen;
235     ossl_ssize_t ret;
236 
237     if (ilen == 0)
238         return 0;
239 
240     if (BrotliEncoderCompress(BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW,
241                               BROTLI_DEFAULT_MODE, ilen, in,
242                               &out_size, out) == BROTLI_FALSE)
243         return -1;
244 
245     if (out_size > OSSL_SSIZE_MAX)
246         return -1;
247     ret = (ossl_ssize_t)out_size;
248     if (ret < 0)
249         return -1;
250     return ret;
251 }
252 
brotli_oneshot_expand_block(COMP_CTX * ctx,unsigned char * out,size_t olen,unsigned char * in,size_t ilen)253 static ossl_ssize_t brotli_oneshot_expand_block(COMP_CTX *ctx, unsigned char *out,
254                                                 size_t olen, unsigned char *in,
255                                                 size_t ilen)
256 {
257     size_t out_size = olen;
258     ossl_ssize_t ret;
259 
260     if (ilen == 0)
261         return 0;
262 
263     if (BrotliDecoderDecompress(ilen, in, &out_size, out) != BROTLI_DECODER_RESULT_SUCCESS)
264         return -1;
265 
266     if (out_size > OSSL_SSIZE_MAX)
267         return -1;
268     ret = (ossl_ssize_t)out_size;
269     if (ret < 0)
270         return -1;
271     return ret;
272 }
273 
274 static COMP_METHOD brotli_oneshot_method = {
275     NID_brotli,
276     LN_brotli,
277     brotli_oneshot_init,
278     brotli_oneshot_finish,
279     brotli_oneshot_compress_block,
280     brotli_oneshot_expand_block
281 };
282 
283 static CRYPTO_ONCE brotli_once = CRYPTO_ONCE_STATIC_INIT;
DEFINE_RUN_ONCE_STATIC(ossl_comp_brotli_init)284 DEFINE_RUN_ONCE_STATIC(ossl_comp_brotli_init)
285 {
286 # ifdef BROTLI_SHARED
287 #  if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32)
288 #   define LIBBROTLIENC "BROTLIENC"
289 #   define LIBBROTLIDEC "BROTLIDEC"
290 #  else
291 #   define LIBBROTLIENC "brotlienc"
292 #   define LIBBROTLIDEC "brotlidec"
293 #  endif
294 
295     brotli_encode_dso = DSO_load(NULL, LIBBROTLIENC, NULL, 0);
296     if (brotli_encode_dso != NULL) {
297         p_encode_init = (encode_init_ft)DSO_bind_func(brotli_encode_dso, "BrotliEncoderCreateInstance");
298         p_encode_stream = (encode_stream_ft)DSO_bind_func(brotli_encode_dso, "BrotliEncoderCompressStream");
299         p_encode_has_more = (encode_has_more_ft)DSO_bind_func(brotli_encode_dso, "BrotliEncoderHasMoreOutput");
300         p_encode_end = (encode_end_ft)DSO_bind_func(brotli_encode_dso, "BrotliEncoderDestroyInstance");
301         p_encode_oneshot = (encode_oneshot_ft)DSO_bind_func(brotli_encode_dso, "BrotliEncoderCompress");
302     }
303 
304     brotli_decode_dso = DSO_load(NULL, LIBBROTLIDEC, NULL, 0);
305     if (brotli_decode_dso != NULL) {
306         p_decode_init = (decode_init_ft)DSO_bind_func(brotli_decode_dso, "BrotliDecoderCreateInstance");
307         p_decode_stream = (decode_stream_ft)DSO_bind_func(brotli_decode_dso, "BrotliDecoderDecompressStream");
308         p_decode_has_more = (decode_has_more_ft)DSO_bind_func(brotli_decode_dso, "BrotliDecoderHasMoreOutput");
309         p_decode_end = (decode_end_ft)DSO_bind_func(brotli_decode_dso, "BrotliDecoderDestroyInstance");
310         p_decode_error = (decode_error_ft)DSO_bind_func(brotli_decode_dso, "BrotliDecoderGetErrorCode");
311         p_decode_error_string = (decode_error_string_ft)DSO_bind_func(brotli_decode_dso, "BrotliDecoderErrorString");
312         p_decode_is_finished = (decode_is_finished_ft)DSO_bind_func(brotli_decode_dso, "BrotliDecoderIsFinished");
313         p_decode_oneshot = (decode_oneshot_ft)DSO_bind_func(brotli_decode_dso, "BrotliDecoderDecompress");
314     }
315 
316     if (p_encode_init == NULL || p_encode_stream == NULL || p_encode_has_more == NULL
317             || p_encode_end == NULL || p_encode_oneshot == NULL || p_decode_init == NULL
318             || p_decode_stream == NULL || p_decode_has_more == NULL || p_decode_end == NULL
319             || p_decode_error == NULL || p_decode_error_string == NULL || p_decode_is_finished == NULL
320             || p_decode_oneshot == NULL) {
321         ossl_comp_brotli_cleanup();
322         return 0;
323     }
324 # endif
325     return 1;
326 }
327 #endif /* ifndef BROTLI / else */
328 
COMP_brotli(void)329 COMP_METHOD *COMP_brotli(void)
330 {
331     COMP_METHOD *meth = NULL;
332 
333 #ifndef OPENSSL_NO_BROTLI
334     if (RUN_ONCE(&brotli_once, ossl_comp_brotli_init))
335         meth = &brotli_stateful_method;
336 #endif
337     return meth;
338 }
339 
COMP_brotli_oneshot(void)340 COMP_METHOD *COMP_brotli_oneshot(void)
341 {
342     COMP_METHOD *meth = NULL;
343 
344 #ifndef OPENSSL_NO_BROTLI
345     if (RUN_ONCE(&brotli_once, ossl_comp_brotli_init))
346         meth = &brotli_oneshot_method;
347 #endif
348     return meth;
349 }
350 
351 /* Also called from OPENSSL_cleanup() */
ossl_comp_brotli_cleanup(void)352 void ossl_comp_brotli_cleanup(void)
353 {
354 #ifdef BROTLI_SHARED
355     DSO_free(brotli_encode_dso);
356     brotli_encode_dso = NULL;
357     DSO_free(brotli_decode_dso);
358     brotli_decode_dso = NULL;
359     p_encode_init = NULL;
360     p_encode_stream = NULL;
361     p_encode_has_more = NULL;
362     p_encode_end = NULL;
363     p_encode_oneshot = NULL;
364     p_decode_init = NULL;
365     p_decode_stream = NULL;
366     p_decode_has_more = NULL;
367     p_decode_end = NULL;
368     p_decode_error = NULL;
369     p_decode_error_string = NULL;
370     p_decode_is_finished = NULL;
371     p_decode_oneshot = NULL;
372 #endif
373 }
374 
375 #ifndef OPENSSL_NO_BROTLI
376 
377 /* Brotli-based compression/decompression filter BIO */
378 
379 typedef struct {
380     struct { /* input structure */
381         size_t avail_in;
382         unsigned char *next_in;
383         size_t avail_out;
384         unsigned char *next_out;
385         unsigned char *buf;
386         size_t bufsize;
387         BrotliDecoderState *state;
388     } decode;
389     struct { /* output structure */
390         size_t avail_in;
391         unsigned char *next_in;
392         size_t avail_out;
393         unsigned char *next_out;
394         unsigned char *buf;
395         size_t bufsize;
396         BrotliEncoderState *state;
397         int mode;                      /* Encoder mode to use */
398         int done;
399         unsigned char *ptr;
400         size_t count;
401     } encode;
402 } BIO_BROTLI_CTX;
403 
404 # define BROTLI_DEFAULT_BUFSIZE 1024
405 
406 static int bio_brotli_new(BIO *bi);
407 static int bio_brotli_free(BIO *bi);
408 static int bio_brotli_read(BIO *b, char *out, int outl);
409 static int bio_brotli_write(BIO *b, const char *in, int inl);
410 static long bio_brotli_ctrl(BIO *b, int cmd, long num, void *ptr);
411 static long bio_brotli_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp);
412 
413 static const BIO_METHOD bio_meth_brotli = {
414     BIO_TYPE_COMP,
415     "brotli",
416     /* TODO: Convert to new style write function */
417     bwrite_conv,
418     bio_brotli_write,
419     /* TODO: Convert to new style read function */
420     bread_conv,
421     bio_brotli_read,
422     NULL,                      /* bio_brotli_puts, */
423     NULL,                      /* bio_brotli_gets, */
424     bio_brotli_ctrl,
425     bio_brotli_new,
426     bio_brotli_free,
427     bio_brotli_callback_ctrl
428 };
429 #endif
430 
BIO_f_brotli(void)431 const BIO_METHOD *BIO_f_brotli(void)
432 {
433 #ifndef OPENSSL_NO_BROTLI
434     if (RUN_ONCE(&brotli_once, ossl_comp_brotli_init))
435         return &bio_meth_brotli;
436 #endif
437     return NULL;
438 }
439 
440 #ifndef OPENSSL_NO_BROTLI
441 
bio_brotli_new(BIO * bi)442 static int bio_brotli_new(BIO *bi)
443 {
444     BIO_BROTLI_CTX *ctx;
445 
446 # ifdef BROTLI_SHARED
447     if (!RUN_ONCE(&brotli_once, ossl_comp_brotli_init)) {
448         ERR_raise(ERR_LIB_COMP, COMP_R_BROTLI_NOT_SUPPORTED);
449         return 0;
450     }
451 # endif
452     ctx = OPENSSL_zalloc(sizeof(*ctx));
453     if (ctx == NULL) {
454         ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE);
455         return 0;
456     }
457     ctx->decode.bufsize = BROTLI_DEFAULT_BUFSIZE;
458     ctx->decode.state = BrotliDecoderCreateInstance(brotli_alloc, brotli_free, NULL);
459     if (ctx->decode.state == NULL)
460         goto err;
461     ctx->encode.bufsize = BROTLI_DEFAULT_BUFSIZE;
462     ctx->encode.state = BrotliEncoderCreateInstance(brotli_alloc, brotli_free, NULL);
463     if (ctx->encode.state == NULL)
464         goto err;
465     ctx->encode.mode = BROTLI_DEFAULT_MODE;
466     BIO_set_init(bi, 1);
467     BIO_set_data(bi, ctx);
468 
469     return 1;
470 
471  err:
472     ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE);
473     BrotliDecoderDestroyInstance(ctx->decode.state);
474     BrotliEncoderDestroyInstance(ctx->encode.state);
475     OPENSSL_free(ctx);
476     return 0;
477 }
478 
bio_brotli_free(BIO * bi)479 static int bio_brotli_free(BIO *bi)
480 {
481     BIO_BROTLI_CTX *ctx;
482 
483     if (bi == NULL)
484         return 0;
485 
486     ctx = BIO_get_data(bi);
487     if (ctx != NULL) {
488         BrotliDecoderDestroyInstance(ctx->decode.state);
489         OPENSSL_free(ctx->decode.buf);
490         BrotliEncoderDestroyInstance(ctx->encode.state);
491         OPENSSL_free(ctx->encode.buf);
492         OPENSSL_free(ctx);
493     }
494     BIO_set_data(bi, NULL);
495     BIO_set_init(bi, 0);
496 
497     return 1;
498 }
499 
bio_brotli_read(BIO * b,char * out,int outl)500 static int bio_brotli_read(BIO *b, char *out, int outl)
501 {
502     BIO_BROTLI_CTX *ctx;
503     BrotliDecoderResult bret;
504     int ret;
505     BIO *next = BIO_next(b);
506 
507     if (out == NULL || outl <= 0) {
508         ERR_raise(ERR_LIB_COMP, ERR_R_PASSED_INVALID_ARGUMENT);
509         return 0;
510     }
511 #if INT_MAX > SIZE_MAX
512     if ((unsigned int)outl > SIZE_MAX) {
513         ERR_raise(ERR_LIB_COMP, ERR_R_PASSED_INVALID_ARGUMENT);
514         return 0;
515     }
516 #endif
517 
518     ctx = BIO_get_data(b);
519     BIO_clear_retry_flags(b);
520     if (ctx->decode.buf == NULL) {
521         ctx->decode.buf = OPENSSL_malloc(ctx->decode.bufsize);
522         if (ctx->decode.buf == NULL) {
523             ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE);
524             return 0;
525         }
526         ctx->decode.next_in = ctx->decode.buf;
527         ctx->decode.avail_in = 0;
528     }
529 
530     /* Copy output data directly to supplied buffer */
531     ctx->decode.next_out = (unsigned char *)out;
532     ctx->decode.avail_out = (size_t)outl;
533     for (;;) {
534         /* Decompress while data available */
535         while (ctx->decode.avail_in > 0 || BrotliDecoderHasMoreOutput(ctx->decode.state)) {
536             bret = BrotliDecoderDecompressStream(ctx->decode.state, &ctx->decode.avail_in, (const uint8_t**)&ctx->decode.next_in,
537                                                   &ctx->decode.avail_out, &ctx->decode.next_out, NULL);
538             if (bret == BROTLI_DECODER_RESULT_ERROR) {
539                 ERR_raise(ERR_LIB_COMP, COMP_R_BROTLI_DECODE_ERROR);
540                 ERR_add_error_data(1, BrotliDecoderErrorString(BrotliDecoderGetErrorCode(ctx->decode.state)));
541                 return 0;
542             }
543             /* If EOF or we've read everything then return */
544             if (BrotliDecoderIsFinished(ctx->decode.state) || ctx->decode.avail_out == 0)
545                 return (int)(outl - ctx->decode.avail_out);
546         }
547 
548         /* If EOF */
549         if (BrotliDecoderIsFinished(ctx->decode.state))
550             return 0;
551 
552         /*
553          * No data in input buffer try to read some in, if an error then
554          * return the total data read.
555          */
556         ret = BIO_read(next, ctx->decode.buf, ctx->decode.bufsize);
557         if (ret <= 0) {
558             /* Total data read */
559             int tot = outl - ctx->decode.avail_out;
560 
561             BIO_copy_next_retry(b);
562             if (ret < 0)
563                 return (tot > 0) ? tot : ret;
564             return tot;
565         }
566         ctx->decode.avail_in = ret;
567         ctx->decode.next_in = ctx->decode.buf;
568     }
569 }
570 
bio_brotli_write(BIO * b,const char * in,int inl)571 static int bio_brotli_write(BIO *b, const char *in, int inl)
572 {
573     BIO_BROTLI_CTX *ctx;
574     BROTLI_BOOL brret;
575     int ret;
576     BIO *next = BIO_next(b);
577 
578     if (in == NULL || inl <= 0) {
579         ERR_raise(ERR_LIB_COMP, ERR_R_PASSED_INVALID_ARGUMENT);
580         return 0;
581     }
582 #if INT_MAX > SIZE_MAX
583     if ((unsigned int)inl > SIZE_MAX) {
584         ERR_raise(ERR_LIB_COMP, ERR_R_PASSED_INVALID_ARGUMENT);
585         return 0;
586     }
587 #endif
588 
589     ctx = BIO_get_data(b);
590     if (ctx->encode.done)
591         return 0;
592 
593     BIO_clear_retry_flags(b);
594     if (ctx->encode.buf == NULL) {
595         ctx->encode.buf = OPENSSL_malloc(ctx->encode.bufsize);
596         if (ctx->encode.buf == NULL) {
597             ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE);
598             return 0;
599         }
600         ctx->encode.ptr = ctx->encode.buf;
601         ctx->encode.count = 0;
602         ctx->encode.next_out = ctx->encode.buf;
603         ctx->encode.avail_out = ctx->encode.bufsize;
604     }
605     /* Obtain input data directly from supplied buffer */
606     ctx->encode.next_in = (unsigned char *)in;
607     ctx->encode.avail_in = (size_t)inl;
608     for (;;) {
609         /* If data in output buffer write it first */
610         while (ctx->encode.count > 0) {
611             ret = BIO_write(next, ctx->encode.ptr, ctx->encode.count);
612             if (ret <= 0) {
613                 /* Total data written */
614                 int tot = inl - ctx->encode.avail_in;
615 
616                 BIO_copy_next_retry(b);
617                 if (ret < 0)
618                     return (tot > 0) ? tot : ret;
619                 return tot;
620             }
621             ctx->encode.ptr += ret;
622             ctx->encode.count -= ret;
623         }
624 
625         /* Have we consumed all supplied data? */
626         if (ctx->encode.avail_in == 0 && !BrotliEncoderHasMoreOutput(ctx->encode.state))
627             return inl;
628 
629         /* Compress some more */
630 
631         /* Reset buffer */
632         ctx->encode.ptr = ctx->encode.buf;
633         ctx->encode.next_out = ctx->encode.buf;
634         ctx->encode.avail_out = ctx->encode.bufsize;
635         /* Compress some more */
636         brret = BrotliEncoderCompressStream(ctx->encode.state, BROTLI_OPERATION_FLUSH, &ctx->encode.avail_in, (const uint8_t**)&ctx->encode.next_in,
637                                             &ctx->encode.avail_out, &ctx->encode.next_out, NULL);
638         if (brret != BROTLI_TRUE) {
639             ERR_raise(ERR_LIB_COMP, COMP_R_BROTLI_ENCODE_ERROR);
640             ERR_add_error_data(1, "brotli encoder error");
641             return 0;
642         }
643         ctx->encode.count = ctx->encode.bufsize - ctx->encode.avail_out;
644     }
645 }
646 
bio_brotli_flush(BIO * b)647 static int bio_brotli_flush(BIO *b)
648 {
649     BIO_BROTLI_CTX *ctx;
650     BROTLI_BOOL brret;
651     int ret;
652     BIO *next = BIO_next(b);
653 
654     ctx = BIO_get_data(b);
655 
656     /* If no data written or already flush show success */
657     if (ctx->encode.buf == NULL || (ctx->encode.done && ctx->encode.count == 0))
658         return 1;
659 
660     BIO_clear_retry_flags(b);
661     /* No more input data */
662     ctx->encode.next_in = NULL;
663     ctx->encode.avail_in = 0;
664     for (;;) {
665         /* If data in output buffer write it first */
666         while (ctx->encode.count > 0) {
667             ret = BIO_write(next, ctx->encode.ptr, ctx->encode.count);
668             if (ret <= 0) {
669                 BIO_copy_next_retry(b);
670                 return ret;
671             }
672             ctx->encode.ptr += ret;
673             ctx->encode.count -= ret;
674         }
675         if (ctx->encode.done)
676             return 1;
677 
678         /* Compress some more */
679 
680         /* Reset buffer */
681         ctx->encode.ptr = ctx->encode.buf;
682         ctx->encode.next_out = ctx->encode.buf;
683         ctx->encode.avail_out = ctx->encode.bufsize;
684         /* Compress some more */
685         brret = BrotliEncoderCompressStream(ctx->encode.state, BROTLI_OPERATION_FINISH, &ctx->encode.avail_in,
686                                             (const uint8_t**)&ctx->encode.next_in, &ctx->encode.avail_out, &ctx->encode.next_out, NULL);
687         if (brret != BROTLI_TRUE) {
688             ERR_raise(ERR_LIB_COMP, COMP_R_BROTLI_DECODE_ERROR);
689             ERR_add_error_data(1, "brotli encoder error");
690             return 0;
691         }
692         if (!BrotliEncoderHasMoreOutput(ctx->encode.state) && ctx->encode.avail_in == 0)
693             ctx->encode.done = 1;
694         ctx->encode.count = ctx->encode.bufsize - ctx->encode.avail_out;
695     }
696 }
697 
bio_brotli_ctrl(BIO * b,int cmd,long num,void * ptr)698 static long bio_brotli_ctrl(BIO *b, int cmd, long num, void *ptr)
699 {
700     BIO_BROTLI_CTX *ctx;
701     unsigned char *tmp;
702     int ret = 0, *ip;
703     size_t ibs, obs;
704     BIO *next = BIO_next(b);
705 
706     if (next == NULL)
707         return 0;
708     ctx = BIO_get_data(b);
709     switch (cmd) {
710 
711     case BIO_CTRL_RESET:
712         ctx->encode.count = 0;
713         ctx->encode.done = 0;
714         ret = 1;
715         break;
716 
717     case BIO_CTRL_FLUSH:
718         ret = bio_brotli_flush(b);
719         if (ret > 0) {
720             ret = BIO_flush(next);
721             BIO_copy_next_retry(b);
722         }
723         break;
724 
725     case BIO_C_SET_BUFF_SIZE:
726         ibs = ctx->decode.bufsize;
727         obs = ctx->encode.bufsize;
728         if (ptr != NULL) {
729             ip = ptr;
730             if (*ip == 0)
731                 ibs = (size_t)num;
732             else
733                 obs = (size_t)num;
734         } else {
735             ibs = (size_t)num;
736             obs = ibs;
737         }
738 
739         if (ibs > 0 && ibs != ctx->decode.bufsize) {
740             /* Do not free/alloc, only reallocate */
741             if (ctx->decode.buf != NULL) {
742                 tmp = OPENSSL_realloc(ctx->decode.buf, ibs);
743                 if (tmp == NULL)
744                     return 0;
745                 ctx->decode.buf = tmp;
746             }
747             ctx->decode.bufsize = ibs;
748         }
749 
750         if (obs > 0 && obs != ctx->encode.bufsize) {
751             /* Do not free/alloc, only reallocate */
752             if (ctx->encode.buf != NULL) {
753                 tmp = OPENSSL_realloc(ctx->encode.buf, obs);
754                 if (tmp == NULL)
755                     return 0;
756                 ctx->encode.buf = tmp;
757             }
758             ctx->encode.bufsize = obs;
759         }
760         ret = 1;
761         break;
762 
763     case BIO_C_DO_STATE_MACHINE:
764         BIO_clear_retry_flags(b);
765         ret = BIO_ctrl(next, cmd, num, ptr);
766         BIO_copy_next_retry(b);
767         break;
768 
769    case BIO_CTRL_WPENDING:
770         if (BrotliEncoderHasMoreOutput(ctx->encode.state))
771             ret = 1;
772         else
773             ret = BIO_ctrl(next, cmd, num, ptr);
774         break;
775 
776     case BIO_CTRL_PENDING:
777         if (!BrotliDecoderIsFinished(ctx->decode.state))
778             ret = 1;
779         else
780             ret = BIO_ctrl(next, cmd, num, ptr);
781         break;
782 
783     default:
784         ret = BIO_ctrl(next, cmd, num, ptr);
785         break;
786 
787     }
788 
789     return ret;
790 }
791 
bio_brotli_callback_ctrl(BIO * b,int cmd,BIO_info_cb * fp)792 static long bio_brotli_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp)
793 {
794     BIO *next = BIO_next(b);
795     if (next == NULL)
796         return 0;
797     return BIO_callback_ctrl(next, cmd, fp);
798 }
799 
800 #endif
801