summaryrefslogtreecommitdiffstats
path: root/crypto/bio/bss_bio.c
diff options
context:
space:
mode:
authorBodo Möller <bodo@openssl.org>1999-06-12 01:03:40 +0000
committerBodo Möller <bodo@openssl.org>1999-06-12 01:03:40 +0000
commit95d29597b7cc2ec3653811b1a659094b4831f96b (patch)
tree0959b7b48af2cbf172b0f6a2ab35f86e503c3ef6 /crypto/bio/bss_bio.c
parent9bce3070acf81a2890ec7a6c94b97094691b5038 (diff)
BIO pairs.
Diffstat (limited to 'crypto/bio/bss_bio.c')
-rw-r--r--crypto/bio/bss_bio.c349
1 files changed, 326 insertions, 23 deletions
diff --git a/crypto/bio/bss_bio.c b/crypto/bio/bss_bio.c
index f026d51807..ea9d5c714a 100644
--- a/crypto/bio/bss_bio.c
+++ b/crypto/bio/bss_bio.c
@@ -1,11 +1,11 @@
/* 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_)
{
- /* XXX */
- return -1;
+ 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)
+static int bio_write(BIO *bio, char *buf, int num_)
{
- /* XXX */
- return -1;
- }
+ 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)
+ {
+ /* we already closed */
+ BIOerr(BIO_F_BIO_WRITE, BIO_R_BROKEN_PIPE);
+ return -1;
+ }
+
+ assert(b->len <= b->size);
+
+ if (b->len == b->size)
+ {
+ BIO_set_retry_write(bio); /* buffer is full */
+ return -1;
+ }
+
+ /* we can write */
+ if (num > b->size - b->len)
+ num = b->size - b->len;
+
+ /* now write "num" bytes */
+
+ rest = num;
-static long bio_ctrl(BIO *bio, int cmd, long num, char *ptr)
+ 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);
+ }