/* crypto/bio/bss_bio.c -*- Mode: C; c-file-style: "eay" -*- */
/* Special method for a BIO where the other endpoint is also a BIO
* 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.
* See ssl/ssltest.c for some hints on how this can be used. */
#ifndef BIO_PAIR_DEBUG
# undef NDEBUG /* avoid conflicting definitions */
# define NDEBUG
#endif
#include <assert.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/err.h>
#include <openssl/crypto.h>
#include "e_os.h"
#ifndef SSIZE_MAX
# define SSIZE_MAX INT_MAX
#endif
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, const char *buf, int num);
static long bio_ctrl(BIO *bio, int cmd, long num, void *ptr);
static int bio_puts(BIO *bio, const char *str);
static int bio_make_pair(BIO *bio1, BIO *bio2);
static void bio_destroy_pair(BIO *bio);
static BIO_METHOD methods_biop =
{
BIO_TYPE_BIO,
"BIO pair",
bio_write,
bio_read,
bio_puts,
NULL /* no bio_gets */,
bio_ctrl,
bio_new,
bio_free,
NULL /* no bio_callback_ctrl */
};
BIO_METHOD *BIO_s_bio(void)
{
return &methods_biop;
}
struct bio_bio_st
{
BIO *peer; /* NULL if buf == NULL.
* If peer != NULL, then peer->ptr is also a bio_bio_st,
* and its "peer" member points back to us.
* peer != NULL iff init != 0 in the BIO. */
/* This is for what we write (i.e. reading uses peer's struct): */
int closed; /* valid iff peer != NULL */
size_t len; /* valid iff buf != NULL; 0 if peer == NULL */
size_t offset; /* valid iff buf != NULL; 0 if len == 0 */
size_t size;
char *buf; /* "size" elements (if != NULL) */
size_t request; /* valid iff peer != NULL; 0 if len != 0,
* otherwise set by peer to number of bytes
* it (unsuccessfully) tried to read,
* never more than buffer space (size-len) warrants. */
};
static int bio_new(BIO *bio)
{
struct bio_bio_st *b;
b = OPENSSL_malloc(sizeof *b);
if (b == NULL)
return 0;
b->peer = NULL;
b->size = 17*1024; /* enough for one TLS record (just a default) */
b->buf = NULL;
bio->ptr = b;
return 1;
}
static int bio_free(BIO *bio)
{
struct bio_bio_st *b;
if (bio == NULL)
return 0;
b = bio->ptr;
assert(b != NULL);
if (b->peer)
bio_destroy_pair(bio);
if (b->buf != NULL)
{
OPENSSL_free(b->buf);
}
OPENSSL_free(b);
return 1;
}
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
/* don't ask for more than the peer can
* deliver in one write */
peer_b->request = peer_b->size;
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