Commit 95d29597 authored by Bodo Möller's avatar Bodo Möller
Browse files

BIO pairs.

parent 9bce3070
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -4,6 +4,16 @@

 Changes between 0.9.3a and 0.9.4

  *) Add missing case to s3_clnt.c state machine -- one of the new SSL tests
     through a BIO pair triggered the default case, i.e.
     SSLerr(...,SSL_R_UNKNOWN_STATE).
     [Bodo Moeller]

  *) New "BIO pair" concept (crypto/bio/bss_bio.c) so that applications
     can use the SSL library even if none of the specific BIOs is
     appropriate.
     [Bodo Moeller]

  *) Fix a bug in i2d_DSAPublicKey() which meant it returned the wrong value
     for the encoded length.
     [Jeon KyoungHo <khjeon@sds.samsung.co.kr>]
+35 −1
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ extern "C" {
#endif

#include <stdio.h>
#include <stdlib.h>
#include <openssl/crypto.h>

/* These are the 'types' of BIOs */
@@ -317,6 +318,15 @@ typedef struct bio_f_buffer_ctx_struct
#define BIO_C_GET_SOCKS				134
#define BIO_C_SET_SOCKS				135

#define BIO_C_SET_WRITE_BUF_SIZE		136/* for BIO_s_bio */
#define BIO_C_GET_WRITE_BUF_SIZE		137
#define BIO_C_MAKE_BIO_PAIR			138
#define BIO_C_DESTROY_BIO_PAIR			139
#define BIO_C_GET_WRITE_GUARANTEE		140
#define BIO_C_GET_READ_REQUEST			141
#define BIO_C_SHUTDOWN_WR			142


#define BIO_set_app_data(s,arg)		BIO_set_ex_data(s,0,(char *)arg)
#define BIO_get_app_data(s)		BIO_get_ex_data(s,0)

@@ -431,6 +441,9 @@ int BIO_read_filename(BIO *b,const char *name);
#define BIO_get_close(b)	(int)BIO_ctrl(b,BIO_CTRL_GET_CLOSE,0,NULL)
#define BIO_pending(b)		(int)BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL)
#define BIO_wpending(b)		(int)BIO_ctrl(b,BIO_CTRL_WPENDING,0,NULL)
/* ...pending macros have inappropriate return type */
size_t BIO_ctrl_pending(BIO *b);
size_t BIO_ctrl_wpending(BIO *b);
#define BIO_flush(b)		(int)BIO_ctrl(b,BIO_CTRL_FLUSH,0,NULL)
#define BIO_get_info_callback(b,cbp) (int)BIO_ctrl(b,BIO_CTRL_GET_CALLBACK,0,(char *)cbp)
#define BIO_set_info_callback(b,cb) (int)BIO_ctrl(b,BIO_CTRL_SET_CALLBACK,0,(char *)cb)
@@ -438,6 +451,19 @@ int BIO_read_filename(BIO *b,const char *name);
/* For the BIO_f_buffer() type */
#define BIO_buffer_get_num_lines(b) BIO_ctrl(b,BIO_CTRL_GET,0,NULL)

/* For BIO_s_bio() */
#define BIO_set_write_buf_size(b,size) (int)BIO_ctrl(b,BIO_C_SET_WRITE_BUF_SIZE,size,NULL)
#define BIO_get_write_buf_size(b,size) (size_t)BIO_ctrl(b,BIO_C_GET_WRITE_BUF_SIZE,size,NULL)
#define BIO_make_bio_pair(b1,b2)   (int)BIO_ctrl(b1,BIO_C_MAKE_BIO_PAIR,0,b2)
#define BIO_destroy_bio_pair(b)    (int)BIO_ctrl(b,BIO_C_DESTROY_BIO_PAIR,0,NULL)
/* macros with inappropriate type -- but ...pending macros use int too: */
#define BIO_get_write_guarantee(b) (int)BIO_ctrl(b,BIO_C_GET_WRITE_GUARANTEE,0,NULL)
#define BIO_get_read_request(b)    (int)BIO_ctrl(b,BIO_C_GET_READ_REQUEST,0,NULL)
size_t BIO_ctrl_get_write_guarantee(BIO *b);
size_t BIO_ctrl_get_read_request(BIO *b);



#ifdef NO_STDIO
#define NO_FP_API
#endif
@@ -473,7 +499,7 @@ int BIO_read(BIO *b, void *data, int len);
int	BIO_gets(BIO *bp,char *buf, int size);
int	BIO_write(BIO *b, const char *data, int len);
int	BIO_puts(BIO *bp,const char *buf);
long	BIO_ctrl(BIO *bp,int cmd,long larg,char *parg);
long	BIO_ctrl(BIO *bp,int cmd,long larg,void *parg);
char *	BIO_ptr_ctrl(BIO *bp,int cmd,long larg);
long	BIO_int_ctrl(BIO *bp,int cmd,long larg,int iarg);
BIO *	BIO_push(BIO *b,BIO *append);
@@ -538,6 +564,13 @@ BIO *BIO_new_fd(int fd, int close_flag);
BIO *BIO_new_connect(char *host_port);
BIO *BIO_new_accept(char *host_port);

int BIO_new_bio_pair(BIO **bio1, size_t writebuf1,
	BIO **bio2, size_t writebuf2);
/* If successful, returns 1 and in *bio1, *bio2 two BIO pair endpoints.
 * Otherwise returns 0 and sets *bio1 and *bio2 to NULL.
 * Size 0 uses default value.
 */

void BIO_copy_next_retry(BIO *b);

long BIO_ghbn_ctrl(int cmd,int iarg,char *parg);
@@ -579,6 +612,7 @@ int BIO_printf(BIO *bio, ...);
#define BIO_R_ACCEPT_ERROR				 100
#define BIO_R_BAD_FOPEN_MODE				 101
#define BIO_R_BAD_HOSTNAME_LOOKUP			 102
#define BIO_R_BROKEN_PIPE				 124
#define BIO_R_CONNECT_ERROR				 103
#define BIO_R_ERROR_SETTING_NBIO			 104
#define BIO_R_ERROR_SETTING_NBIO_ON_ACCEPTED_SOCKET	 105
+1 −0
Original line number Diff line number Diff line
@@ -95,6 +95,7 @@ static ERR_STRING_DATA BIO_str_reasons[]=
{BIO_R_ACCEPT_ERROR                      ,"accept error"},
{BIO_R_BAD_FOPEN_MODE                    ,"bad fopen mode"},
{BIO_R_BAD_HOSTNAME_LOOKUP               ,"bad hostname lookup"},
{BIO_R_BROKEN_PIPE                       ,"broken pipe"},
{BIO_R_CONNECT_ERROR                     ,"connect error"},
{BIO_R_ERROR_SETTING_NBIO                ,"error setting nbio"},
{BIO_R_ERROR_SETTING_NBIO_ON_ACCEPTED_SOCKET,"error setting nbio on accepted socket"},
+15 −1
Original line number Diff line number Diff line
@@ -290,7 +290,7 @@ char *BIO_ptr_ctrl(BIO *b, int cmd, long larg)
		return(p);
	}

long BIO_ctrl(BIO *b, int cmd, long larg, char *parg)
long BIO_ctrl(BIO *b, int cmd, long larg, void *parg)
	{
	long ret;
	long (*cb)();
@@ -317,6 +317,20 @@ long BIO_ctrl(BIO *b, int cmd, long larg, char *parg)
	return(ret);
	}

/* It is unfortunate to duplicate in functions what the BIO_(w)pending macros
 * do; but those macros have inappropriate return type, and for interfacing
 * from other programming languages, C macros aren't much of a help anyway. */
size_t BIO_ctrl_pending(BIO *bio)
    {
	return BIO_ctrl(bio, BIO_CTRL_PENDING, 0, NULL);
	}

size_t BIO_ctrl_wpending(BIO *bio)
    {
	return BIO_ctrl(bio, BIO_CTRL_WPENDING, 0, NULL);
	}


/* put the 'bio' on the end of b's list of operators */
BIO *BIO_push(BIO *b, BIO *bio)
	{
+326 −23
Original line number Diff line number Diff line
/* crypto/bio/bss_bio.c  -*- Mode: C; c-file-style: "eay" -*- */

/*  *** Not yet finished (or even tested). *** */

/* Special method for a BIO where the other endpoint is also a BIO
 * of this kind, handled by the same thread.
 * of this kind, handled by the same thread (i.e. the "peer" is actually
 * ourselves, wearing a different hat).
 * Such "BIO pairs" are mainly for using the SSL library with I/O interfaces
 * for which no specific BIO method is available. */
 * for which no specific BIO method is available.
 * See ssl/ssltest.c for some hints on how this can be used. */

#include <assert.h>
#include <stdlib.h>
@@ -19,7 +19,7 @@ static int bio_new(BIO *bio);
static int bio_free(BIO *bio);
static int bio_read(BIO *bio, char *buf, int size);
static int bio_write(BIO *bio, char *buf, int num);
static long bio_ctrl(BIO *bio, int cmd, long num, char *ptr);
static long bio_ctrl(BIO *bio, int cmd, long num, void *ptr);
static int bio_puts(BIO *bio, char *str);

static int bio_make_pair(BIO *bio1, BIO *bio2);
@@ -74,6 +74,7 @@ static int bio_new(BIO *bio)
	b->size = 17*1024; /* enough for one TLS record (just a default) */
	b->buf = NULL;

	bio->ptr = b;
	return 1;
	}

@@ -103,19 +104,167 @@ static int bio_free(BIO *bio)



static int bio_read(BIO *bio, char *buf, int size)
static int bio_read(BIO *bio, char *buf, int size_)
	{
	size_t size = size_;
	size_t rest;
	struct bio_bio_st *b, *peer_b;

	BIO_clear_retry_flags(bio);

	if (!bio->init)
		return 0;

	b = bio->ptr;
	assert(b != NULL);
	assert(b->peer != NULL);
	peer_b = b->peer->ptr;
	assert(peer_b != NULL);
	assert(peer_b->buf != NULL);

	peer_b->request = 0; /* will be set in "retry_read" situation */

	if (buf == NULL || size == 0)
		return 0;

	if (peer_b->len == 0)
		{
		if (peer_b->closed)
			return 0; /* writer has closed, and no data is left */
		else
			{
			BIO_set_retry_read(bio); /* buffer is empty */
			if (size <= peer_b->size)
				peer_b->request = size;
			else
				peer_b->request = peer_b->size; /* don't ask for more than
				                                 * the peer can deliver
				                                 * in one write */
			return -1;
			}
		}

	/* we can read */
	if (peer_b->len < size)
		size = peer_b->len;

	/* now read "size" bytes */
	
	rest = size;
	
	assert(rest > 0);
	do /* one or two iterations */
		{
		size_t chunk;
		
		assert(rest <= peer_b->len);
		if (peer_b->offset + rest <= peer_b->size)
			chunk = rest;
		else
			/* wrap around ring buffer */
			chunk = peer_b->size - peer_b->offset;
		assert(peer_b->offset + chunk <= peer_b->size);
		
		memcpy(buf, peer_b->buf + peer_b->offset, chunk);
		
		peer_b->len -= chunk;
		if (peer_b->len)
			{
			peer_b->offset += chunk;
			assert(peer_b->offset <= peer_b->size);
			if (peer_b->offset == peer_b->size)
				peer_b->offset = 0;
			buf += chunk;
			}
		else
			{
			/* buffer now empty, no need to advance "buf" */
			assert(chunk == rest);
			peer_b->offset = 0;
			}
		rest -= chunk;
		}
	while (rest);
	
	peer_b->request -= size;
	return size;
	}

static int bio_write(BIO *bio, char *buf, int num_)
	{
	size_t num = num_;
	size_t rest;
	struct bio_bio_st *b;

	BIO_clear_retry_flags(bio);

	if (!bio->init || buf == NULL || num == 0)
		return 0;

	b = bio->ptr;		
	assert(b != NULL);
	assert(b->peer != NULL);
	assert(b->buf != NULL);

	b->request = 0;
	if (b->closed)
		{
	/* XXX */
		/* we already closed */
		BIOerr(BIO_F_BIO_WRITE, BIO_R_BROKEN_PIPE);
		return -1;
		}

static int bio_write(BIO *bio, char *buf, int num)
	assert(b->len <= b->size);

	if (b->len == b->size)
		{
	/* XXX */
		BIO_set_retry_write(bio); /* buffer is full */
		return -1;
		}

static long bio_ctrl(BIO *bio, int cmd, long num, char *ptr)
	/* we can write */
	if (num > b->size - b->len)
		num = b->size - b->len;
	
	/* now write "num" bytes */

	rest = num;
	
	assert(rest > 0);
	do /* one or two iterations */
		{
		size_t write_offset;
		size_t chunk;

		assert(b->len + rest <= b->size);

		write_offset = b->offset + b->len;
		if (write_offset >= b->size)
			write_offset -= b->size;
		/* b->buf[write_offset] is the first byte we can write to. */

		if (write_offset + rest <= b->size)
			chunk = rest;
		else
			/* wrap around ring buffer */
			chunk = b->size - write_offset;
		
		memcpy(b->buf + write_offset, buf, chunk);
		
		b->len += chunk;

		assert(b->len <= b->size);
		
		rest -= chunk;
		buf += chunk;
		}
	while (rest);

	return num;
	}


static long bio_ctrl(BIO *bio, int cmd, long num, void *ptr)
	{
	long ret;
	struct bio_bio_st *b = bio->ptr;
@@ -124,13 +273,76 @@ static long bio_ctrl(BIO *bio, int cmd, long num, char *ptr)

	switch (cmd)
		{
		/* XXX Additional commands: */
		/* - Set buffer size */
		/* - make pair */
		/* - destroy pair */
		/* - get number of bytes that the next write will accept */
		/* - get number of bytes requested by peer */
		/* - send "close" */
	/* specific CTRL codes */

	case BIO_C_SET_WRITE_BUF_SIZE:
		if (b->peer)
			{
			BIOerr(BIO_F_BIO_CTRL, BIO_R_IN_USE);
			ret = 0;
			}
		else
			{
			size_t new_size = num;

			if (b->size != new_size)
				{
				if (b->buf) 
					{
					Free(b->buf);
					b->buf = NULL;
					}
				b->size = new_size;
				}
			ret = 1;
			}
		break;

	case BIO_C_GET_WRITE_BUF_SIZE:
		num = (long) b->size;

	case BIO_C_MAKE_BIO_PAIR:
		{
		BIO *other_bio = ptr;
		
		if (bio_make_pair(bio, other_bio))
			ret = 1;
		else
			ret = 0;
		}
		break;
		
	case BIO_C_DESTROY_BIO_PAIR:
		/* Effects both BIOs in the pair -- call just once!
		 * Or let BIO_free(bio1); BIO_free(bio2); do the job. */
		bio_destroy_pair(bio);
		ret = 1;
		break;

	case BIO_C_GET_WRITE_GUARANTEE:
		/* How many bytes can the caller feed to the next write
		 * withouth having to keep any? */
		if (b->peer == NULL || b->closed)
			ret = 0;
		else
			ret = (long) b->size - b->len;
		break;

	case BIO_C_GET_READ_REQUEST:
		/* If the peer unsuccesfully tried to read, how many bytes
		 * were requested?  (As with BIO_CTRL_PENDING, that number
		 * can usually be treated as boolean.) */
		ret = (long) b->request;
		break;

	case BIO_C_SHUTDOWN_WR:
		/* similar to shutdown(..., SHUT_WR) */
		b->closed = 1;
		ret = 1;
		break;


	/* standard CTRL codes follow */

	case BIO_CTRL_RESET:
		if (b->buf != NULL)
@@ -169,7 +381,20 @@ static long bio_ctrl(BIO *bio, int cmd, long num, char *ptr)
		break;

	case BIO_CTRL_DUP:
		/* XXX */
		/* See BIO_dup_chain for circumstances we have to expect. */
		{
		BIO *other_bio = ptr;
		struct bio_bio_st *other_b;
		
		assert(other_bio != NULL);
		other_b = other_bio->ptr;
		assert(other_b != NULL);
		
		assert(other_b->buf == NULL); /* other_bio is always fresh */

		other_b->size = b->size;
		}

		ret = 1;
		break;

@@ -177,6 +402,22 @@ static long bio_ctrl(BIO *bio, int cmd, long num, char *ptr)
		ret = 1;
		break;

	case BIO_CTRL_EOF:
		{
		BIO *other_bio = ptr;
		
		if (other_bio)
			{
			struct bio_bio_st *other_b = other_bio->ptr;
			
			assert(other_b != NULL);
			ret = other_b->len == 0 && other_b->closed;
			}
		else
			ret = 1;
		}
		break;

	default:
		ret = 0;
		}
@@ -188,8 +429,6 @@ static int bio_puts(BIO *bio, char *str)
	return bio_write(bio, str, strlen(str));
	}

/* Until bio_make_pair is used, make a dummy function use it for -pedantic */
void dummy() { bio_make_pair(NULL,NULL); }

static int bio_make_pair(BIO *bio1, BIO *bio2)
	{
@@ -273,3 +512,67 @@ static void bio_destroy_pair(BIO *bio)
			}
		}
	}
 

/* Exported convenience functions */
int BIO_new_bio_pair(BIO **bio1_p, size_t writebuf1,
	BIO **bio2_p, size_t writebuf2)
	 {
	 BIO *bio1 = NULL, *bio2 = NULL;
	 long r;
	 int ret = 0;

	 bio1 = BIO_new(BIO_s_bio());
	 if (bio1 == NULL)
		 goto err;
	 bio2 = BIO_new(BIO_s_bio());
	 if (bio2 == NULL)
		 goto err;

	 if (writebuf1)
		 {
		 r = BIO_set_write_buf_size(bio1, writebuf1);
		 if (!r)
			 goto err;
		 }
	 if (writebuf2)
		 {
		 r = BIO_set_write_buf_size(bio2, writebuf2);
		 if (!r)
			 goto err;
		 }

	 r = BIO_make_bio_pair(bio1, bio2);
	 if (!r)
		 goto err;
	 ret = 1;

 err:
	 if (ret == 0)
		 {
		 if (bio1)
			 {
			 BIO_free(bio1);
			 bio1 = NULL;
			 }
		 if (bio2)
			 {
			 BIO_free(bio2);
			 bio2 = NULL;
			 }
		 }

	 *bio1_p = bio1;
	 *bio2_p = bio2;
	 return ret;
	 }

size_t BIO_ctrl_get_write_guarantee(BIO *bio)
    {
	return BIO_ctrl(bio, BIO_C_GET_WRITE_GUARANTEE, 0, NULL);
	}

size_t BIO_ctrl_read_request(BIO *bio)
    {
	return BIO_ctrl(bio, BIO_C_GET_READ_REQUEST, 0, NULL);
	}
Loading