bio_b64.c revision 68651
155714Skris/* crypto/evp/bio_b64.c */
255714Skris/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
355714Skris * All rights reserved.
455714Skris *
555714Skris * This package is an SSL implementation written
655714Skris * by Eric Young (eay@cryptsoft.com).
755714Skris * The implementation was written so as to conform with Netscapes SSL.
855714Skris *
955714Skris * This library is free for commercial and non-commercial use as long as
1055714Skris * the following conditions are aheared to.  The following conditions
1155714Skris * apply to all code found in this distribution, be it the RC4, RSA,
1255714Skris * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
1355714Skris * included with this distribution is covered by the same copyright terms
1455714Skris * except that the holder is Tim Hudson (tjh@cryptsoft.com).
1555714Skris *
1655714Skris * Copyright remains Eric Young's, and as such any Copyright notices in
1755714Skris * the code are not to be removed.
1855714Skris * If this package is used in a product, Eric Young should be given attribution
1955714Skris * as the author of the parts of the library used.
2055714Skris * This can be in the form of a textual message at program startup or
2155714Skris * in documentation (online or textual) provided with the package.
2255714Skris *
2355714Skris * Redistribution and use in source and binary forms, with or without
2455714Skris * modification, are permitted provided that the following conditions
2555714Skris * are met:
2655714Skris * 1. Redistributions of source code must retain the copyright
2755714Skris *    notice, this list of conditions and the following disclaimer.
2855714Skris * 2. Redistributions in binary form must reproduce the above copyright
2955714Skris *    notice, this list of conditions and the following disclaimer in the
3055714Skris *    documentation and/or other materials provided with the distribution.
3155714Skris * 3. All advertising materials mentioning features or use of this software
3255714Skris *    must display the following acknowledgement:
3355714Skris *    "This product includes cryptographic software written by
3455714Skris *     Eric Young (eay@cryptsoft.com)"
3555714Skris *    The word 'cryptographic' can be left out if the rouines from the library
3655714Skris *    being used are not cryptographic related :-).
3755714Skris * 4. If you include any Windows specific code (or a derivative thereof) from
3855714Skris *    the apps directory (application code) you must include an acknowledgement:
3955714Skris *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
4055714Skris *
4155714Skris * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
4255714Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4355714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4455714Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
4555714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
4655714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
4755714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4855714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
4955714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5055714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5155714Skris * SUCH DAMAGE.
5255714Skris *
5355714Skris * The licence and distribution terms for any publically available version or
5455714Skris * derivative of this code cannot be changed.  i.e. this code cannot simply be
5555714Skris * copied and put under another distribution licence
5655714Skris * [including the GNU Public Licence.]
5755714Skris */
5855714Skris
5955714Skris#include <stdio.h>
6055714Skris#include <errno.h>
6155714Skris#include "cryptlib.h"
6255714Skris#include <openssl/buffer.h>
6355714Skris#include <openssl/evp.h>
6455714Skris
6568651Skrisstatic int b64_write(BIO *h, const char *buf, int num);
6668651Skrisstatic int b64_read(BIO *h, char *buf, int size);
6768651Skris/*static int b64_puts(BIO *h, const char *str); */
6868651Skris/*static int b64_gets(BIO *h, char *str, int size); */
6968651Skrisstatic long b64_ctrl(BIO *h, int cmd, long arg1, void *arg2);
7055714Skrisstatic int b64_new(BIO *h);
7155714Skrisstatic int b64_free(BIO *data);
7268651Skrisstatic long b64_callback_ctrl(BIO *h,int cmd,bio_info_cb *fp);
7355714Skris#define B64_BLOCK_SIZE	1024
7455714Skris#define B64_BLOCK_SIZE2	768
7555714Skris#define B64_NONE	0
7655714Skris#define B64_ENCODE	1
7755714Skris#define B64_DECODE	2
7855714Skris
7955714Skristypedef struct b64_struct
8055714Skris	{
8155714Skris	/*BIO *bio; moved to the BIO structure */
8255714Skris	int buf_len;
8355714Skris	int buf_off;
8455714Skris	int tmp_len;		/* used to find the start when decoding */
8555714Skris	int tmp_nl;		/* If true, scan until '\n' */
8655714Skris	int encode;
8755714Skris	int start;		/* have we started decoding yet? */
8855714Skris	int cont;		/* <= 0 when finished */
8955714Skris	EVP_ENCODE_CTX base64;
9055714Skris	char buf[EVP_ENCODE_LENGTH(B64_BLOCK_SIZE)+10];
9155714Skris	char tmp[B64_BLOCK_SIZE];
9255714Skris	} BIO_B64_CTX;
9355714Skris
9455714Skrisstatic BIO_METHOD methods_b64=
9555714Skris	{
9655714Skris	BIO_TYPE_BASE64,"base64 encoding",
9755714Skris	b64_write,
9855714Skris	b64_read,
9955714Skris	NULL, /* b64_puts, */
10055714Skris	NULL, /* b64_gets, */
10155714Skris	b64_ctrl,
10255714Skris	b64_new,
10355714Skris	b64_free,
10459191Skris	b64_callback_ctrl,
10555714Skris	};
10655714Skris
10755714SkrisBIO_METHOD *BIO_f_base64(void)
10855714Skris	{
10955714Skris	return(&methods_b64);
11055714Skris	}
11155714Skris
11255714Skrisstatic int b64_new(BIO *bi)
11355714Skris	{
11455714Skris	BIO_B64_CTX *ctx;
11555714Skris
11668651Skris	ctx=(BIO_B64_CTX *)OPENSSL_malloc(sizeof(BIO_B64_CTX));
11755714Skris	if (ctx == NULL) return(0);
11855714Skris
11955714Skris	ctx->buf_len=0;
12055714Skris	ctx->tmp_len=0;
12155714Skris	ctx->tmp_nl=0;
12255714Skris	ctx->buf_off=0;
12355714Skris	ctx->cont=1;
12455714Skris	ctx->start=1;
12555714Skris	ctx->encode=0;
12655714Skris
12755714Skris	bi->init=1;
12855714Skris	bi->ptr=(char *)ctx;
12955714Skris	bi->flags=0;
13055714Skris	return(1);
13155714Skris	}
13255714Skris
13355714Skrisstatic int b64_free(BIO *a)
13455714Skris	{
13555714Skris	if (a == NULL) return(0);
13668651Skris	OPENSSL_free(a->ptr);
13755714Skris	a->ptr=NULL;
13855714Skris	a->init=0;
13955714Skris	a->flags=0;
14055714Skris	return(1);
14155714Skris	}
14255714Skris
14355714Skrisstatic int b64_read(BIO *b, char *out, int outl)
14455714Skris	{
14555714Skris	int ret=0,i,ii,j,k,x,n,num,ret_code=0;
14655714Skris	BIO_B64_CTX *ctx;
14755714Skris	unsigned char *p,*q;
14855714Skris
14955714Skris	if (out == NULL) return(0);
15055714Skris	ctx=(BIO_B64_CTX *)b->ptr;
15155714Skris
15255714Skris	if ((ctx == NULL) || (b->next_bio == NULL)) return(0);
15355714Skris
15455714Skris	if (ctx->encode != B64_DECODE)
15555714Skris		{
15655714Skris		ctx->encode=B64_DECODE;
15755714Skris		ctx->buf_len=0;
15855714Skris		ctx->buf_off=0;
15955714Skris		ctx->tmp_len=0;
16055714Skris		EVP_DecodeInit(&(ctx->base64));
16155714Skris		}
16255714Skris
16355714Skris	/* First check if there are bytes decoded/encoded */
16455714Skris	if (ctx->buf_len > 0)
16555714Skris		{
16655714Skris		i=ctx->buf_len-ctx->buf_off;
16755714Skris		if (i > outl) i=outl;
16855714Skris		memcpy(out,&(ctx->buf[ctx->buf_off]),i);
16955714Skris		ret=i;
17055714Skris		out+=i;
17155714Skris		outl-=i;
17255714Skris		ctx->buf_off+=i;
17355714Skris		if (ctx->buf_len == ctx->buf_off)
17455714Skris			{
17555714Skris			ctx->buf_len=0;
17655714Skris			ctx->buf_off=0;
17755714Skris			}
17855714Skris		}
17955714Skris
18055714Skris	/* At this point, we have room of outl bytes and an empty
18155714Skris	 * buffer, so we should read in some more. */
18255714Skris
18355714Skris	ret_code=0;
18455714Skris	while (outl > 0)
18555714Skris		{
18655714Skris		if (ctx->cont <= 0) break;
18755714Skris
18855714Skris		i=BIO_read(b->next_bio,&(ctx->tmp[ctx->tmp_len]),
18955714Skris			B64_BLOCK_SIZE-ctx->tmp_len);
19055714Skris
19155714Skris		if (i <= 0)
19255714Skris			{
19355714Skris			ret_code=i;
19455714Skris
19555714Skris			/* Should be continue next time we are called? */
19655714Skris			if (!BIO_should_retry(b->next_bio))
19755714Skris				ctx->cont=i;
19855714Skris			/* else we should continue when called again */
19955714Skris			break;
20055714Skris			}
20155714Skris		i+=ctx->tmp_len;
20255714Skris
20355714Skris		/* We need to scan, a line at a time until we
20455714Skris		 * have a valid line if we are starting. */
20555714Skris		if (ctx->start && (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL))
20655714Skris			{
20755714Skris			/* ctx->start=1; */
20855714Skris			ctx->tmp_len=0;
20955714Skris			}
21055714Skris		else if (ctx->start)
21155714Skris			{
21255714Skris			q=p=(unsigned char *)ctx->tmp;
21355714Skris			for (j=0; j<i; j++)
21455714Skris				{
21555714Skris				if (*(q++) != '\n') continue;
21655714Skris
21755714Skris				/* due to a previous very long line,
21855714Skris				 * we need to keep on scanning for a '\n'
21955714Skris				 * before we even start looking for
22055714Skris				 * base64 encoded stuff. */
22155714Skris				if (ctx->tmp_nl)
22255714Skris					{
22355714Skris					p=q;
22455714Skris					ctx->tmp_nl=0;
22555714Skris					continue;
22655714Skris					}
22755714Skris
22855714Skris				k=EVP_DecodeUpdate(&(ctx->base64),
22955714Skris					(unsigned char *)ctx->buf,
23055714Skris					&num,p,q-p);
23155714Skris				if ((k <= 0) && (num == 0) && (ctx->start))
23255714Skris					EVP_DecodeInit(&ctx->base64);
23355714Skris				else
23455714Skris					{
23555714Skris					if (p != (unsigned char *)
23655714Skris						&(ctx->tmp[0]))
23755714Skris						{
23855714Skris						i-=(p- (unsigned char *)
23955714Skris							&(ctx->tmp[0]));
24055714Skris						for (x=0; x < i; x++)
24155714Skris							ctx->tmp[x]=p[x];
24255714Skris						}
24359191Skris					EVP_DecodeInit(&ctx->base64);
24455714Skris					ctx->start=0;
24555714Skris					break;
24655714Skris					}
24755714Skris				p=q;
24855714Skris				}
24955714Skris
25055714Skris			/* we fell off the end without starting */
25155714Skris			if (j == i)
25255714Skris				{
25355714Skris				/* Is this is one long chunk?, if so, keep on
25455714Skris				 * reading until a new line. */
25555714Skris				if (p == (unsigned char *)&(ctx->tmp[0]))
25655714Skris					{
25755714Skris					ctx->tmp_nl=1;
25855714Skris					ctx->tmp_len=0;
25955714Skris					}
26055714Skris				else if (p != q) /* finished on a '\n' */
26155714Skris					{
26255714Skris					n=q-p;
26355714Skris					for (ii=0; ii<n; ii++)
26455714Skris						ctx->tmp[ii]=p[ii];
26555714Skris					ctx->tmp_len=n;
26655714Skris					}
26755714Skris				/* else finished on a '\n' */
26855714Skris				continue;
26955714Skris				}
27055714Skris			else
27155714Skris				ctx->tmp_len=0;
27255714Skris			}
27355714Skris
27455714Skris		if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL)
27555714Skris			{
27655714Skris			int z,jj;
27755714Skris
27855714Skris			jj=(i>>2)<<2;
27955714Skris			z=EVP_DecodeBlock((unsigned char *)ctx->buf,
28055714Skris				(unsigned char *)ctx->tmp,jj);
28155714Skris			if (jj > 2)
28255714Skris				{
28355714Skris				if (ctx->tmp[jj-1] == '=')
28455714Skris					{
28555714Skris					z--;
28655714Skris					if (ctx->tmp[jj-2] == '=')
28755714Skris						z--;
28855714Skris					}
28955714Skris				}
29055714Skris			/* z is now number of output bytes and jj is the
29155714Skris			 * number consumed */
29255714Skris			if (jj != i)
29355714Skris				{
29455714Skris				memcpy((unsigned char *)ctx->tmp,
29555714Skris					(unsigned char *)&(ctx->tmp[jj]),i-jj);
29655714Skris				ctx->tmp_len=i-jj;
29755714Skris				}
29855714Skris			ctx->buf_len=0;
29955714Skris			if (z > 0)
30055714Skris				{
30155714Skris				ctx->buf_len=z;
30255714Skris				i=1;
30355714Skris				}
30455714Skris			else
30555714Skris				i=z;
30655714Skris			}
30755714Skris		else
30855714Skris			{
30955714Skris			i=EVP_DecodeUpdate(&(ctx->base64),
31055714Skris				(unsigned char *)ctx->buf,&ctx->buf_len,
31155714Skris				(unsigned char *)ctx->tmp,i);
31255714Skris			}
31355714Skris		ctx->cont=i;
31455714Skris		ctx->buf_off=0;
31555714Skris		if (i < 0)
31655714Skris			{
31755714Skris			ret_code=0;
31855714Skris			ctx->buf_len=0;
31955714Skris			break;
32055714Skris			}
32155714Skris
32255714Skris		if (ctx->buf_len <= outl)
32355714Skris			i=ctx->buf_len;
32455714Skris		else
32555714Skris			i=outl;
32655714Skris
32755714Skris		memcpy(out,ctx->buf,i);
32855714Skris		ret+=i;
32955714Skris		ctx->buf_off=i;
33055714Skris		if (ctx->buf_off == ctx->buf_len)
33155714Skris			{
33255714Skris			ctx->buf_len=0;
33355714Skris			ctx->buf_off=0;
33455714Skris			}
33555714Skris		outl-=i;
33655714Skris		out+=i;
33755714Skris		}
33855714Skris	BIO_clear_retry_flags(b);
33955714Skris	BIO_copy_next_retry(b);
34055714Skris	return((ret == 0)?ret_code:ret);
34155714Skris	}
34255714Skris
34368651Skrisstatic int b64_write(BIO *b, const char *in, int inl)
34455714Skris	{
34555714Skris	int ret=inl,n,i;
34655714Skris	BIO_B64_CTX *ctx;
34755714Skris
34855714Skris	ctx=(BIO_B64_CTX *)b->ptr;
34955714Skris	BIO_clear_retry_flags(b);
35055714Skris
35155714Skris	if (ctx->encode != B64_ENCODE)
35255714Skris		{
35355714Skris		ctx->encode=B64_ENCODE;
35455714Skris		ctx->buf_len=0;
35555714Skris		ctx->buf_off=0;
35655714Skris		ctx->tmp_len=0;
35755714Skris		EVP_EncodeInit(&(ctx->base64));
35855714Skris		}
35955714Skris
36055714Skris	n=ctx->buf_len-ctx->buf_off;
36155714Skris	while (n > 0)
36255714Skris		{
36355714Skris		i=BIO_write(b->next_bio,&(ctx->buf[ctx->buf_off]),n);
36455714Skris		if (i <= 0)
36555714Skris			{
36655714Skris			BIO_copy_next_retry(b);
36755714Skris			return(i);
36855714Skris			}
36955714Skris		ctx->buf_off+=i;
37055714Skris		n-=i;
37155714Skris		}
37255714Skris	/* at this point all pending data has been written */
37368651Skris	ctx->buf_off=0;
37468651Skris	ctx->buf_len=0;
37555714Skris
37655714Skris	if ((in == NULL) || (inl <= 0)) return(0);
37755714Skris
37855714Skris	while (inl > 0)
37955714Skris		{
38055714Skris		n=(inl > B64_BLOCK_SIZE)?B64_BLOCK_SIZE:inl;
38155714Skris
38255714Skris		if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL)
38355714Skris			{
38455714Skris			if (ctx->tmp_len > 0)
38555714Skris				{
38655714Skris				n=3-ctx->tmp_len;
38768651Skris				/* There's a teoretical possibility for this */
38868651Skris				if (n > inl)
38968651Skris					n=inl;
39055714Skris				memcpy(&(ctx->tmp[ctx->tmp_len]),in,n);
39155714Skris				ctx->tmp_len+=n;
39268651Skris				if (ctx->tmp_len < 3)
39355714Skris					break;
39455714Skris				ctx->buf_len=EVP_EncodeBlock(
39555714Skris					(unsigned char *)ctx->buf,
39668651Skris					(unsigned char *)ctx->tmp,
39768651Skris					ctx->tmp_len);
39868651Skris				/* Since we're now done using the temporary
39968651Skris				   buffer, the length should be 0'd */
40068651Skris				ctx->tmp_len=0;
40155714Skris				}
40255714Skris			else
40355714Skris				{
40455714Skris				if (n < 3)
40555714Skris					{
40655714Skris					memcpy(&(ctx->tmp[0]),in,n);
40755714Skris					ctx->tmp_len=n;
40855714Skris					break;
40955714Skris					}
41055714Skris				n-=n%3;
41155714Skris				ctx->buf_len=EVP_EncodeBlock(
41255714Skris					(unsigned char *)ctx->buf,
41355714Skris					(unsigned char *)in,n);
41455714Skris				}
41555714Skris			}
41655714Skris		else
41755714Skris			{
41855714Skris			EVP_EncodeUpdate(&(ctx->base64),
41955714Skris				(unsigned char *)ctx->buf,&ctx->buf_len,
42055714Skris				(unsigned char *)in,n);
42155714Skris			}
42255714Skris		inl-=n;
42355714Skris		in+=n;
42455714Skris
42555714Skris		ctx->buf_off=0;
42655714Skris		n=ctx->buf_len;
42755714Skris		while (n > 0)
42855714Skris			{
42955714Skris			i=BIO_write(b->next_bio,&(ctx->buf[ctx->buf_off]),n);
43055714Skris			if (i <= 0)
43155714Skris				{
43255714Skris				BIO_copy_next_retry(b);
43355714Skris				return((ret == 0)?i:ret);
43455714Skris				}
43555714Skris			n-=i;
43655714Skris			ctx->buf_off+=i;
43755714Skris			}
43855714Skris		ctx->buf_len=0;
43955714Skris		ctx->buf_off=0;
44055714Skris		}
44155714Skris	return(ret);
44255714Skris	}
44355714Skris
44468651Skrisstatic long b64_ctrl(BIO *b, int cmd, long num, void *ptr)
44555714Skris	{
44655714Skris	BIO_B64_CTX *ctx;
44755714Skris	long ret=1;
44855714Skris	int i;
44955714Skris
45055714Skris	ctx=(BIO_B64_CTX *)b->ptr;
45155714Skris
45255714Skris	switch (cmd)
45355714Skris		{
45455714Skris	case BIO_CTRL_RESET:
45555714Skris		ctx->cont=1;
45655714Skris		ctx->start=1;
45755714Skris		ctx->encode=B64_NONE;
45855714Skris		ret=BIO_ctrl(b->next_bio,cmd,num,ptr);
45955714Skris		break;
46055714Skris	case BIO_CTRL_EOF:	/* More to read */
46155714Skris		if (ctx->cont <= 0)
46255714Skris			ret=1;
46355714Skris		else
46455714Skris			ret=BIO_ctrl(b->next_bio,cmd,num,ptr);
46555714Skris		break;
46655714Skris	case BIO_CTRL_WPENDING: /* More to write in buffer */
46755714Skris		ret=ctx->buf_len-ctx->buf_off;
46855714Skris		if ((ret == 0) && (ctx->base64.num != 0))
46955714Skris			ret=1;
47055714Skris		else if (ret <= 0)
47155714Skris			ret=BIO_ctrl(b->next_bio,cmd,num,ptr);
47255714Skris		break;
47355714Skris	case BIO_CTRL_PENDING: /* More to read in buffer */
47455714Skris		ret=ctx->buf_len-ctx->buf_off;
47555714Skris		if (ret <= 0)
47655714Skris			ret=BIO_ctrl(b->next_bio,cmd,num,ptr);
47755714Skris		break;
47855714Skris	case BIO_CTRL_FLUSH:
47955714Skris		/* do a final write */
48055714Skrisagain:
48155714Skris		while (ctx->buf_len != ctx->buf_off)
48255714Skris			{
48355714Skris			i=b64_write(b,NULL,0);
48455714Skris			if (i < 0)
48555714Skris				{
48655714Skris				ret=i;
48755714Skris				break;
48855714Skris				}
48955714Skris			}
49055714Skris		if (BIO_get_flags(b) & BIO_FLAGS_BASE64_NO_NL)
49155714Skris			{
49255714Skris			if (ctx->tmp_len != 0)
49355714Skris				{
49455714Skris				ctx->buf_len=EVP_EncodeBlock(
49555714Skris					(unsigned char *)ctx->buf,
49655714Skris					(unsigned char *)ctx->tmp,
49755714Skris					ctx->tmp_len);
49855714Skris				ctx->buf_off=0;
49955714Skris				ctx->tmp_len=0;
50055714Skris				goto again;
50155714Skris				}
50255714Skris			}
50355714Skris		else if (ctx->base64.num != 0)
50455714Skris			{
50555714Skris			ctx->buf_off=0;
50655714Skris			EVP_EncodeFinal(&(ctx->base64),
50755714Skris				(unsigned char *)ctx->buf,
50855714Skris				&(ctx->buf_len));
50955714Skris			/* push out the bytes */
51055714Skris			goto again;
51155714Skris			}
51255714Skris		/* Finally flush the underlying BIO */
51355714Skris		ret=BIO_ctrl(b->next_bio,cmd,num,ptr);
51455714Skris		break;
51555714Skris
51655714Skris	case BIO_C_DO_STATE_MACHINE:
51755714Skris		BIO_clear_retry_flags(b);
51855714Skris		ret=BIO_ctrl(b->next_bio,cmd,num,ptr);
51955714Skris		BIO_copy_next_retry(b);
52055714Skris		break;
52155714Skris
52255714Skris	case BIO_CTRL_DUP:
52355714Skris		break;
52455714Skris	case BIO_CTRL_INFO:
52555714Skris	case BIO_CTRL_GET:
52655714Skris	case BIO_CTRL_SET:
52755714Skris	default:
52855714Skris		ret=BIO_ctrl(b->next_bio,cmd,num,ptr);
52955714Skris		break;
53055714Skris		}
53155714Skris	return(ret);
53255714Skris	}
53355714Skris
53468651Skrisstatic long b64_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
53559191Skris	{
53659191Skris	long ret=1;
53759191Skris
53859191Skris	if (b->next_bio == NULL) return(0);
53959191Skris	switch (cmd)
54059191Skris		{
54159191Skris	default:
54259191Skris		ret=BIO_callback_ctrl(b->next_bio,cmd,fp);
54359191Skris		break;
54459191Skris		}
54559191Skris	return(ret);
54659191Skris	}
54759191Skris
548