Remake the way dynamic zlib is loaded
[openssl.git] / crypto / comp / c_zlib.c
index 941b807eb39149f2d1e3fae2378676f6c8734407..6dd76846c5d74a229e77a50687032adc5857ee5c 100644 (file)
+/* ====================================================================
+ * Copyright (c) 1999-2015 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <openssl/objects.h>
-#include <openssl/comp.h>
+#include "internal/comp.h"
 #include <openssl/err.h>
+#include "internal/cryptlib_int.h"
+#include "internal/bio.h"
+#include "comp_lcl.h"
 
-COMP_METHOD *COMP_zlib(void );
+COMP_METHOD *COMP_zlib(void);
 
-static COMP_METHOD zlib_method_nozlib={
-       NID_undef,
-       "(undef)",
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       };
+static COMP_METHOD zlib_method_nozlib = {
+    NID_undef,
+    "(undef)",
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+};
 
 #ifndef ZLIB
-#undef ZLIB_SHARED
+# undef ZLIB_SHARED
 #else
 
-#include <zlib.h>
+# include <zlib.h>
 
 static int zlib_stateful_init(COMP_CTX *ctx);
 static void zlib_stateful_finish(COMP_CTX *ctx);
 static int zlib_stateful_compress_block(COMP_CTX *ctx, unsigned char *out,
-       unsigned int olen, unsigned char *in, unsigned int ilen);
+                                        unsigned int olen, unsigned char *in,
+                                        unsigned int ilen);
 static int zlib_stateful_expand_block(COMP_CTX *ctx, unsigned char *out,
-       unsigned int olen, unsigned char *in, unsigned int ilen);
-
-#if 0
-static int zlib_compress_block(COMP_CTX *ctx, unsigned char *out,
-       unsigned int olen, unsigned char *in, unsigned int ilen);
-static int zlib_expand_block(COMP_CTX *ctx, unsigned char *out,
-       unsigned int olen, unsigned char *in, unsigned int ilen);
-
-static int zz_uncompress(Bytef *dest, uLongf *destLen, const Bytef *source,
-       uLong sourceLen);
-
-static COMP_METHOD zlib_stateless_method={
-       NID_zlib_compression,
-       LN_zlib_compression,
-       NULL,
-       NULL,
-       zlib_compress_block,
-       zlib_expand_block,
-       NULL,
-       NULL,
-       };
-#endif
+                                      unsigned int olen, unsigned char *in,
+                                      unsigned int ilen);
+
+/* memory allocations functions for zlib initialisation */
+static void *zlib_zalloc(void *opaque, unsigned int no, unsigned int size)
+{
+    void *p;
+
+    p = OPENSSL_zalloc(no * size);
+    return p;
+}
+
+static void zlib_zfree(void *opaque, void *address)
+{
+    OPENSSL_free(address);
+}
+
+
+static COMP_METHOD zlib_stateful_method = {
+    NID_zlib_compression,
+    LN_zlib_compression,
+    zlib_stateful_init,
+    zlib_stateful_finish,
+    zlib_stateful_compress_block,
+    zlib_stateful_expand_block
+};
 
-static COMP_METHOD zlib_stateful_method={
-       NID_zlib_compression,
-       LN_zlib_compression,
-       zlib_stateful_init,
-       zlib_stateful_finish,
-       zlib_stateful_compress_block,
-       zlib_stateful_expand_block,
-       NULL,
-       NULL,
-       };
-
-/* 
+/*
  * When OpenSSL is built on Windows, we do not want to require that
  * the ZLIB.DLL be available in order for the OpenSSL DLLs to
  * work.  Therefore, all ZLIB routines are loaded at run time
  * and we do not link to a .LIB file when ZLIB_SHARED is set.
  */
-#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32)
-# include <windows.h>
-#endif /* !(OPENSSL_SYS_WINDOWS || OPENSSL_SYS_WIN32) */
+# if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32)
+#  include <windows.h>
+# endif                         /* !(OPENSSL_SYS_WINDOWS ||
+                                 * OPENSSL_SYS_WIN32) */
 
-#ifdef ZLIB_SHARED
-#include <openssl/dso.h>
+# ifdef ZLIB_SHARED
+#  include "internal/dso.h"
 
 /* Function pointers */
-typedef int (*compress_ft)(Bytef *dest,uLongf *destLen,
-       const Bytef *source, uLong sourceLen);
-typedef int (*inflateEnd_ft)(z_streamp strm);
-typedef int (*inflate_ft)(z_streamp strm, int flush);
-typedef int (*inflateInit__ft)(z_streamp strm,
-       const char * version, int stream_size);
-typedef int (*deflateEnd_ft)(z_streamp strm);
-typedef int (*deflate_ft)(z_streamp strm, int flush);
-typedef int (*deflateInit__ft)(z_streamp strm, int level,
-       const char * version, int stream_size);
-static compress_ft     p_compress=NULL;
-static inflateEnd_ft   p_inflateEnd=NULL;
-static inflate_ft      p_inflate=NULL;
-static inflateInit__ft p_inflateInit_=NULL;
-static deflateEnd_ft   p_deflateEnd=NULL;
-static deflate_ft      p_deflate=NULL;
-static deflateInit__ft p_deflateInit_=NULL;
+typedef int (*compress_ft) (Bytef *dest, uLongf * destLen,
+                            const Bytef *source, uLong sourceLen);
+typedef int (*inflateEnd_ft) (z_streamp strm);
+typedef int (*inflate_ft) (z_streamp strm, int flush);
+typedef int (*inflateInit__ft) (z_streamp strm,
+                                const char *version, int stream_size);
+typedef int (*deflateEnd_ft) (z_streamp strm);
+typedef int (*deflate_ft) (z_streamp strm, int flush);
+typedef int (*deflateInit__ft) (z_streamp strm, int level,
+                                const char *version, int stream_size);
+typedef const char *(*zError__ft) (int err);
+static compress_ft p_compress = NULL;
+static inflateEnd_ft p_inflateEnd = NULL;
+static inflate_ft p_inflate = NULL;
+static inflateInit__ft p_inflateInit_ = NULL;
+static deflateEnd_ft p_deflateEnd = NULL;
+static deflate_ft p_deflate = NULL;
+static deflateInit__ft p_deflateInit_ = NULL;
+static zError__ft p_zError = NULL;
 
 static int zlib_loaded = 0;     /* only attempt to init func pts once */
 static DSO *zlib_dso = NULL;
 
-#define compress                p_compress
-#define inflateEnd              p_inflateEnd
-#define inflate                 p_inflate
-#define inflateInit_            p_inflateInit_
-#define deflateEnd              p_deflateEnd
-#define deflate                 p_deflate
-#define deflateInit_            p_deflateInit_
-#endif /* ZLIB_SHARED */
-
-struct zlib_state
-       {
-       z_stream istream;
-       z_stream ostream;
-       };
-
-static int zlib_stateful_ex_idx = -1;
-
-static void zlib_stateful_free_ex_data(void *obj, void *item,
-       CRYPTO_EX_DATA *ad, int ind,long argl, void *argp)
-       {
-       struct zlib_state *state = (struct zlib_state *)item;
-       inflateEnd(&state->istream);
-       deflateEnd(&state->ostream);
-       OPENSSL_free(state);
-       }
+#  define compress                p_compress
+#  define inflateEnd              p_inflateEnd
+#  define inflate                 p_inflate
+#  define inflateInit_            p_inflateInit_
+#  define deflateEnd              p_deflateEnd
+#  define deflate                 p_deflate
+#  define deflateInit_            p_deflateInit_
+#  define zError                  p_zError
+# endif                         /* ZLIB_SHARED */
+
+struct zlib_state {
+    z_stream istream;
+    z_stream ostream;
+};
 
 static int zlib_stateful_init(COMP_CTX *ctx)
-       {
-       int err;
-       struct zlib_state *state =
-               (struct zlib_state *)OPENSSL_malloc(sizeof(struct zlib_state));
-
-       if (state == NULL)
-               goto err;
-
-       state->istream.zalloc = Z_NULL;
-       state->istream.zfree = Z_NULL;
-       state->istream.opaque = Z_NULL;
-       state->istream.next_in = Z_NULL;
-       state->istream.next_out = Z_NULL;
-       state->istream.avail_in = 0;
-       state->istream.avail_out = 0;
-       err = inflateInit_(&state->istream,
-               ZLIB_VERSION, sizeof(z_stream));
-       if (err != Z_OK)
-               goto err;
-
-       state->ostream.zalloc = Z_NULL;
-       state->ostream.zfree = Z_NULL;
-       state->ostream.opaque = Z_NULL;
-       state->ostream.next_in = Z_NULL;
-       state->ostream.next_out = Z_NULL;
-       state->ostream.avail_in = 0;
-       state->ostream.avail_out = 0;
-       err = deflateInit_(&state->ostream,Z_DEFAULT_COMPRESSION,
-               ZLIB_VERSION, sizeof(z_stream));
-       if (err != Z_OK)
-               goto err;
-
-       CRYPTO_new_ex_data(CRYPTO_EX_INDEX_COMP,ctx,&ctx->ex_data);
-       if (zlib_stateful_ex_idx == -1)
-               {
-               CRYPTO_w_lock(CRYPTO_LOCK_COMP);
-               if (zlib_stateful_ex_idx == -1)
-                       zlib_stateful_ex_idx =
-                               CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_COMP,
-                                       0,NULL,NULL,NULL,zlib_stateful_free_ex_data);
-               CRYPTO_w_unlock(CRYPTO_LOCK_COMP);
-               if (zlib_stateful_ex_idx == -1)
-                       goto err;
-               }
-       CRYPTO_set_ex_data(&ctx->ex_data,zlib_stateful_ex_idx,state);
-       return 1;
+{
+    int err;
+    struct zlib_state *state = OPENSSL_zalloc(sizeof(*state));
+
+    if (state == NULL)
+        goto err;
+
+    state->istream.zalloc = zlib_zalloc;
+    state->istream.zfree = zlib_zfree;
+    state->istream.opaque = Z_NULL;
+    state->istream.next_in = Z_NULL;
+    state->istream.next_out = Z_NULL;
+    err = inflateInit_(&state->istream, ZLIB_VERSION, sizeof(z_stream));
+    if (err != Z_OK)
+        goto err;
+
+    state->ostream.zalloc = zlib_zalloc;
+    state->ostream.zfree = zlib_zfree;
+    state->ostream.opaque = Z_NULL;
+    state->ostream.next_in = Z_NULL;
+    state->ostream.next_out = Z_NULL;
+    err = deflateInit_(&state->ostream, Z_DEFAULT_COMPRESSION,
+                       ZLIB_VERSION, sizeof(z_stream));
+    if (err != Z_OK)
+        goto err;
+
+    ctx->data = state;
+    return 1;
  err:
-       if (state) OPENSSL_free(state);
-       return 0;
-       }
+    OPENSSL_free(state);
+    return 0;
+}
 
 static void zlib_stateful_finish(COMP_CTX *ctx)
-       {
-       CRYPTO_free_ex_data(CRYPTO_EX_INDEX_COMP,ctx,&ctx->ex_data);
-       }
+{
+    struct zlib_state *state = ctx->data;
+    inflateEnd(&state->istream);
+    deflateEnd(&state->ostream);
+    OPENSSL_free(state);
+}
 
 static int zlib_stateful_compress_block(COMP_CTX *ctx, unsigned char *out,
-       unsigned int olen, unsigned char *in, unsigned int ilen)
-       {
-       int err = Z_OK;
-       struct zlib_state *state =
-               (struct zlib_state *)CRYPTO_get_ex_data(&ctx->ex_data,
-                       zlib_stateful_ex_idx);
-
-       if (state == NULL)
-               return -1;
-
-       state->ostream.next_in = in;
-       state->ostream.avail_in = ilen;
-       state->ostream.next_out = out;
-       state->ostream.avail_out = olen;
-       if (ilen > 0)
-               err = deflate(&state->ostream, Z_SYNC_FLUSH);
-       if (err != Z_OK)
-               return -1;
-#ifdef DEBUG_ZLIB
-       fprintf(stderr,"compress(%4d)->%4d %s\n",
-               ilen,olen - state->ostream.avail_out,
-               (ilen != olen - state->ostream.avail_out)?"zlib":"clear");
-#endif
-       return olen - state->ostream.avail_out;
-       }
+                                        unsigned int olen, unsigned char *in,
+                                        unsigned int ilen)
+{
+    int err = Z_OK;
+    struct zlib_state *state = ctx->data;
+
+    if (state == NULL)
+        return -1;
+
+    state->ostream.next_in = in;
+    state->ostream.avail_in = ilen;
+    state->ostream.next_out = out;
+    state->ostream.avail_out = olen;
+    if (ilen > 0)
+        err = deflate(&state->ostream, Z_SYNC_FLUSH);
+    if (err != Z_OK)
+        return -1;
+    return olen - state->ostream.avail_out;
+}
 
 static int zlib_stateful_expand_block(COMP_CTX *ctx, unsigned char *out,
-       unsigned int olen, unsigned char *in, unsigned int ilen)
-       {
-       int err = Z_OK;
-
-       struct zlib_state *state =
-               (struct zlib_state *)CRYPTO_get_ex_data(&ctx->ex_data,
-                       zlib_stateful_ex_idx);
-
-       if (state == NULL)
-               return 0;
-
-       state->istream.next_in = in;
-       state->istream.avail_in = ilen;
-       state->istream.next_out = out;
-       state->istream.avail_out = olen;
-       if (ilen > 0)
-               err = inflate(&state->istream, Z_SYNC_FLUSH);
-       if (err != Z_OK)
-               return -1;
-#ifdef DEBUG_ZLIB
-       fprintf(stderr,"expand(%4d)->%4d %s\n",
-               ilen,olen - state->istream.avail_out,
-               (ilen != olen - state->istream.avail_out)?"zlib":"clear");
+                                      unsigned int olen, unsigned char *in,
+                                      unsigned int ilen)
+{
+    int err = Z_OK;
+    struct zlib_state *state = ctx->data;
+
+    if (state == NULL)
+        return 0;
+
+    state->istream.next_in = in;
+    state->istream.avail_in = ilen;
+    state->istream.next_out = out;
+    state->istream.avail_out = olen;
+    if (ilen > 0)
+        err = inflate(&state->istream, Z_SYNC_FLUSH);
+    if (err != Z_OK)
+        return -1;
+    return olen - state->istream.avail_out;
+}
+
 #endif
-       return olen - state->istream.avail_out;
-       }
-
-#if 0
-static int zlib_compress_block(COMP_CTX *ctx, unsigned char *out,
-       unsigned int olen, unsigned char *in, unsigned int ilen)
-       {
-       unsigned long l;
-       int i;
-       int clear=1;
-
-       if (ilen > 128)
-               {
-               out[0]=1;
-               l=olen-1;
-               i=compress(&(out[1]),&l,in,(unsigned long)ilen);
-               if (i != Z_OK)
-                       return(-1);
-               if (ilen > l)
-                       {
-                       clear=0;
-                       l++;
-                       }
-               }
-       if (clear)
-               {
-               out[0]=0;
-               memcpy(&(out[1]),in,ilen);
-               l=ilen+1;
-               }
-#ifdef DEBUG_ZLIB
-       fprintf(stderr,"compress(%4d)->%4d %s\n",
-               ilen,(int)l,(clear)?"clear":"zlib");
+
+COMP_METHOD *COMP_zlib(void)
+{
+    COMP_METHOD *meth = &zlib_method_nozlib;
+
+#ifdef ZLIB_SHARED
+    /* LIBZ may be externally defined, and we should respect that value */
+# ifndef LIBZ
+#  if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32)
+#   define LIBZ "ZLIB1"
+#  elif defined(OPENSSL_SYS_VMS)
+#   define LIBZ "LIBZ"
+#  else
+#   define LIBZ "z"
+#  endif
+# endif
+
+    if (!zlib_loaded) {
+        zlib_dso = DSO_load(NULL, LIBZ, NULL, 0);
+        if (zlib_dso != NULL) {
+            p_compress = (compress_ft) DSO_bind_func(zlib_dso, "compress");
+            p_inflateEnd
+                = (inflateEnd_ft) DSO_bind_func(zlib_dso, "inflateEnd");
+            p_inflate = (inflate_ft) DSO_bind_func(zlib_dso, "inflate");
+            p_inflateInit_
+                = (inflateInit__ft) DSO_bind_func(zlib_dso, "inflateInit_");
+            p_deflateEnd
+                = (deflateEnd_ft) DSO_bind_func(zlib_dso, "deflateEnd");
+            p_deflate = (deflate_ft) DSO_bind_func(zlib_dso, "deflate");
+            p_deflateInit_
+                = (deflateInit__ft) DSO_bind_func(zlib_dso, "deflateInit_");
+            p_zError = (zError__ft) DSO_bind_func(zlib_dso, "zError");
+
+            if (p_compress && p_inflateEnd && p_inflate
+                && p_inflateInit_ && p_deflateEnd
+                && p_deflate && p_deflateInit_ && p_zError)
+                zlib_loaded++;
+
+            if (!OPENSSL_init_crypto(OPENSSL_INIT_ZLIB, NULL)) {
+                comp_zlib_cleanup_int();
+                return meth;
+            }
+            if (zlib_loaded)
+                meth = &zlib_stateful_method;
+        }
+    }
 #endif
-       return((int)l);
-       }
-
-static int zlib_expand_block(COMP_CTX *ctx, unsigned char *out,
-       unsigned int olen, unsigned char *in, unsigned int ilen)
-       {
-       unsigned long l;
-       int i;
-
-       if (in[0])
-               {
-               l=olen;
-               i=zz_uncompress(out,&l,&(in[1]),(unsigned long)ilen-1);
-               if (i != Z_OK)
-                       return(-1);
-               }
-       else
-               {
-               memcpy(out,&(in[1]),ilen-1);
-               l=ilen-1;
-               }
-#ifdef DEBUG_ZLIB
-        fprintf(stderr,"expand  (%4d)->%4d %s\n",
-               ilen,(int)l,in[0]?"zlib":"clear");
+#if defined(ZLIB)
+    meth = &zlib_stateful_method;
 #endif
-       return((int)l);
-       }
 
-static int zz_uncompress (Bytef *dest, uLongf *destLen, const Bytef *source,
-            uLong sourceLen)
+    return (meth);
+}
+
+void comp_zlib_cleanup_int(void)
 {
-    z_stream stream;
-    int err;
+#ifdef ZLIB_SHARED
+    if (zlib_dso != NULL)
+        DSO_free(zlib_dso);
+    zlib_dso = NULL;
+#endif
+}
 
-    stream.next_in = (Bytef*)source;
-    stream.avail_in = (uInt)sourceLen;
-    /* Check for source > 64K on 16-bit machine: */
-    if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+#ifdef ZLIB
+
+/* Zlib based compression/decompression filter BIO */
+
+typedef struct {
+    unsigned char *ibuf;        /* Input buffer */
+    int ibufsize;               /* Buffer size */
+    z_stream zin;               /* Input decompress context */
+    unsigned char *obuf;        /* Output buffer */
+    int obufsize;               /* Output buffer size */
+    unsigned char *optr;        /* Position in output buffer */
+    int ocount;                 /* Amount of data in output buffer */
+    int odone;                  /* deflate EOF */
+    int comp_level;             /* Compression level to use */
+    z_stream zout;              /* Output compression context */
+} BIO_ZLIB_CTX;
+
+# define ZLIB_DEFAULT_BUFSIZE 1024
+
+static int bio_zlib_new(BIO *bi);
+static int bio_zlib_free(BIO *bi);
+static int bio_zlib_read(BIO *b, char *out, int outl);
+static int bio_zlib_write(BIO *b, const char *in, int inl);
+static long bio_zlib_ctrl(BIO *b, int cmd, long num, void *ptr);
+static long bio_zlib_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp);
+
+static const BIO_METHOD bio_meth_zlib = {
+    BIO_TYPE_COMP,
+    "zlib",
+    bio_zlib_write,
+    bio_zlib_read,
+    NULL,
+    NULL,
+    bio_zlib_ctrl,
+    bio_zlib_new,
+    bio_zlib_free,
+    bio_zlib_callback_ctrl
+};
+
+const BIO_METHOD *BIO_f_zlib(void)
+{
+    return &bio_meth_zlib;
+}
 
-    stream.next_out = dest;
-    stream.avail_out = (uInt)*destLen;
-    if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+static int bio_zlib_new(BIO *bi)
+{
+    BIO_ZLIB_CTX *ctx;
+# ifdef ZLIB_SHARED
+    (void)COMP_zlib();
+    if (!zlib_loaded) {
+        COMPerr(COMP_F_BIO_ZLIB_NEW, COMP_R_ZLIB_NOT_SUPPORTED);
+        return 0;
+    }
+# endif
+    ctx = OPENSSL_zalloc(sizeof(*ctx));
+    if (ctx == NULL) {
+        COMPerr(COMP_F_BIO_ZLIB_NEW, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    ctx->ibufsize = ZLIB_DEFAULT_BUFSIZE;
+    ctx->obufsize = ZLIB_DEFAULT_BUFSIZE;
+    ctx->zin.zalloc = Z_NULL;
+    ctx->zin.zfree = Z_NULL;
+    ctx->zout.zalloc = Z_NULL;
+    ctx->zout.zfree = Z_NULL;
+    ctx->comp_level = Z_DEFAULT_COMPRESSION;
+    BIO_set_init(bi, 1);
+    BIO_set_data(bi, ctx);
+
+    return 1;
+}
 
-    stream.zalloc = (alloc_func)0;
-    stream.zfree = (free_func)0;
+static int bio_zlib_free(BIO *bi)
+{
+    BIO_ZLIB_CTX *ctx;
+    if (!bi)
+        return 0;
+    ctx = BIO_get_data(bi);
+    if (ctx->ibuf) {
+        /* Destroy decompress context */
+        inflateEnd(&ctx->zin);
+        OPENSSL_free(ctx->ibuf);
+    }
+    if (ctx->obuf) {
+        /* Destroy compress context */
+        deflateEnd(&ctx->zout);
+        OPENSSL_free(ctx->obuf);
+    }
+    OPENSSL_free(ctx);
+    BIO_set_data(bi, NULL);
+    BIO_set_init(bi, 0);
 
-    err = inflateInit_(&stream,
-           ZLIB_VERSION, sizeof(z_stream));
-    if (err != Z_OK) return err;
+    return 1;
+}
 
-    err = inflate(&stream, Z_FINISH);
-    if (err != Z_STREAM_END) {
-        inflateEnd(&stream);
-        return err;
+static int bio_zlib_read(BIO *b, char *out, int outl)
+{
+    BIO_ZLIB_CTX *ctx;
+    int ret;
+    z_stream *zin;
+    BIO *next = BIO_next(b);
+
+    if (!out || !outl)
+        return 0;
+    ctx = BIO_get_data(b);
+    zin = &ctx->zin;
+    BIO_clear_retry_flags(b);
+    if (!ctx->ibuf) {
+        ctx->ibuf = OPENSSL_malloc(ctx->ibufsize);
+        if (ctx->ibuf == NULL) {
+            COMPerr(COMP_F_BIO_ZLIB_READ, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        inflateInit(zin);
+        zin->next_in = ctx->ibuf;
+        zin->avail_in = 0;
     }
-    *destLen = stream.total_out;
 
-    err = inflateEnd(&stream);
-    return err;
+    /* Copy output data directly to supplied buffer */
+    zin->next_out = (unsigned char *)out;
+    zin->avail_out = (unsigned int)outl;
+    for (;;) {
+        /* Decompress while data available */
+        while (zin->avail_in) {
+            ret = inflate(zin, 0);
+            if ((ret != Z_OK) && (ret != Z_STREAM_END)) {
+                COMPerr(COMP_F_BIO_ZLIB_READ, COMP_R_ZLIB_INFLATE_ERROR);
+                ERR_add_error_data(2, "zlib error:", zError(ret));
+                return 0;
+            }
+            /* If EOF or we've read everything then return */
+            if ((ret == Z_STREAM_END) || !zin->avail_out)
+                return outl - zin->avail_out;
+        }
+
+        /*
+         * No data in input buffer try to read some in, if an error then
+         * return the total data read.
+         */
+        ret = BIO_read(next, ctx->ibuf, ctx->ibufsize);
+        if (ret <= 0) {
+            /* Total data read */
+            int tot = outl - zin->avail_out;
+            BIO_copy_next_retry(b);
+            if (ret < 0)
+                return (tot > 0) ? tot : ret;
+            return tot;
+        }
+        zin->avail_in = ret;
+        zin->next_in = ctx->ibuf;
+    }
 }
-#endif
 
-#endif
+static int bio_zlib_write(BIO *b, const char *in, int inl)
+{
+    BIO_ZLIB_CTX *ctx;
+    int ret;
+    z_stream *zout;
+    BIO *next = BIO_next(b);
+
+    if (!in || !inl)
+        return 0;
+    ctx = BIO_get_data(b);
+    if (ctx->odone)
+        return 0;
+    zout = &ctx->zout;
+    BIO_clear_retry_flags(b);
+    if (!ctx->obuf) {
+        ctx->obuf = OPENSSL_malloc(ctx->obufsize);
+        /* Need error here */
+        if (ctx->obuf == NULL) {
+            COMPerr(COMP_F_BIO_ZLIB_WRITE, ERR_R_MALLOC_FAILURE);
+            return 0;
+        }
+        ctx->optr = ctx->obuf;
+        ctx->ocount = 0;
+        deflateInit(zout, ctx->comp_level);
+        zout->next_out = ctx->obuf;
+        zout->avail_out = ctx->obufsize;
+    }
+    /* Obtain input data directly from supplied buffer */
+    zout->next_in = (void *)in;
+    zout->avail_in = inl;
+    for (;;) {
+        /* If data in output buffer write it first */
+        while (ctx->ocount) {
+            ret = BIO_write(next, ctx->optr, ctx->ocount);
+            if (ret <= 0) {
+                /* Total data written */
+                int tot = inl - zout->avail_in;
+                BIO_copy_next_retry(b);
+                if (ret < 0)
+                    return (tot > 0) ? tot : ret;
+                return tot;
+            }
+            ctx->optr += ret;
+            ctx->ocount -= ret;
+        }
+
+        /* Have we consumed all supplied data? */
+        if (!zout->avail_in)
+            return inl;
+
+        /* Compress some more */
+
+        /* Reset buffer */
+        ctx->optr = ctx->obuf;
+        zout->next_out = ctx->obuf;
+        zout->avail_out = ctx->obufsize;
+        /* Compress some more */
+        ret = deflate(zout, 0);
+        if (ret != Z_OK) {
+            COMPerr(COMP_F_BIO_ZLIB_WRITE, COMP_R_ZLIB_DEFLATE_ERROR);
+            ERR_add_error_data(2, "zlib error:", zError(ret));
+            return 0;
+        }
+        ctx->ocount = ctx->obufsize - zout->avail_out;
+    }
+}
 
-COMP_METHOD *COMP_zlib(void)
-       {
-       COMP_METHOD *meth = &zlib_method_nozlib;
+static int bio_zlib_flush(BIO *b)
+{
+    BIO_ZLIB_CTX *ctx;
+    int ret;
+    z_stream *zout;
+    BIO *next = BIO_next(b);
+
+    ctx = BIO_get_data(b);
+    /* If no data written or already flush show success */
+    if (!ctx->obuf || (ctx->odone && !ctx->ocount))
+        return 1;
+    zout = &ctx->zout;
+    BIO_clear_retry_flags(b);
+    /* No more input data */
+    zout->next_in = NULL;
+    zout->avail_in = 0;
+    for (;;) {
+        /* If data in output buffer write it first */
+        while (ctx->ocount) {
+            ret = BIO_write(next, ctx->optr, ctx->ocount);
+            if (ret <= 0) {
+                BIO_copy_next_retry(b);
+                return ret;
+            }
+            ctx->optr += ret;
+            ctx->ocount -= ret;
+        }
+        if (ctx->odone)
+            return 1;
+
+        /* Compress some more */
+
+        /* Reset buffer */
+        ctx->optr = ctx->obuf;
+        zout->next_out = ctx->obuf;
+        zout->avail_out = ctx->obufsize;
+        /* Compress some more */
+        ret = deflate(zout, Z_FINISH);
+        if (ret == Z_STREAM_END)
+            ctx->odone = 1;
+        else if (ret != Z_OK) {
+            COMPerr(COMP_F_BIO_ZLIB_FLUSH, COMP_R_ZLIB_DEFLATE_ERROR);
+            ERR_add_error_data(2, "zlib error:", zError(ret));
+            return 0;
+        }
+        ctx->ocount = ctx->obufsize - zout->avail_out;
+    }
+}
 
-#ifdef ZLIB_SHARED
-       if (!zlib_loaded)
-               {
-#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32)
-               zlib_dso = DSO_load(NULL, "ZLIB1", NULL, 0);
-#else
-               zlib_dso = DSO_load(NULL, "z", NULL, 0);
-#endif
-               if (zlib_dso != NULL)
-                       {
-                       p_compress
-                               = (compress_ft) DSO_bind_func(zlib_dso,
-                                       "compress");
-                       p_inflateEnd
-                               = (inflateEnd_ft) DSO_bind_func(zlib_dso,
-                                       "inflateEnd");
-                       p_inflate
-                               = (inflate_ft) DSO_bind_func(zlib_dso,
-                                       "inflate");
-                       p_inflateInit_
-                               = (inflateInit__ft) DSO_bind_func(zlib_dso,
-                                       "inflateInit_");
-                       p_deflateEnd
-                               = (deflateEnd_ft) DSO_bind_func(zlib_dso,
-                                       "deflateEnd");
-                       p_deflate
-                               = (deflate_ft) DSO_bind_func(zlib_dso,
-                                       "deflate");
-                       p_deflateInit_
-                               = (deflateInit__ft) DSO_bind_func(zlib_dso,
-                                       "deflateInit_");
-
-                       if (p_compress && p_inflateEnd && p_inflate
-                               && p_inflateInit_ && p_deflateEnd
-                               && p_deflate && p_deflateInit_)
-                               zlib_loaded++;
-                       }
-               }
+static long bio_zlib_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+    BIO_ZLIB_CTX *ctx;
+    int ret, *ip;
+    int ibs, obs;
+    BIO *next = BIO_next(b);
+
+    if (next == NULL)
+        return 0;
+    ctx = BIO_get_data(b);
+    switch (cmd) {
+
+    case BIO_CTRL_RESET:
+        ctx->ocount = 0;
+        ctx->odone = 0;
+        ret = 1;
+        break;
+
+    case BIO_CTRL_FLUSH:
+        ret = bio_zlib_flush(b);
+        if (ret > 0)
+            ret = BIO_flush(next);
+        break;
+
+    case BIO_C_SET_BUFF_SIZE:
+        ibs = -1;
+        obs = -1;
+        if (ptr != NULL) {
+            ip = ptr;
+            if (*ip == 0)
+                ibs = (int)num;
+            else
+                obs = (int)num;
+        } else {
+            ibs = (int)num;
+            obs = ibs;
+        }
+
+        if (ibs != -1) {
+            OPENSSL_free(ctx->ibuf);
+            ctx->ibuf = NULL;
+            ctx->ibufsize = ibs;
+        }
+
+        if (obs != -1) {
+            OPENSSL_free(ctx->obuf);
+            ctx->obuf = NULL;
+            ctx->obufsize = obs;
+        }
+        ret = 1;
+        break;
+
+    case BIO_C_DO_STATE_MACHINE:
+        BIO_clear_retry_flags(b);
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        BIO_copy_next_retry(b);
+        break;
+
+    default:
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
 
-#endif
-#ifdef ZLIB_SHARED
-       if (zlib_loaded)
-#endif
-#if defined(ZLIB) || defined(ZLIB_SHARED)
-               meth = &zlib_stateful_method;
-#endif
+    }
 
-       return(meth);
-       }
+    return ret;
+}
 
+static long bio_zlib_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
+{
+    BIO *next = BIO_next(b);
+    if (next == NULL)
+        return 0;
+    return BIO_callback_ctrl(next, cmd, fp);
+}
+
+#endif