xref: /freebsd/crypto/openssl/crypto/bio/bss_bio.c (revision e71b70530d95c4f34d8bdbd78d1242df1ba4a945)
1*e71b7053SJung-uk Kim /*
2*e71b7053SJung-uk Kim  * Copyright 1999-2017 The OpenSSL Project Authors. All Rights Reserved.
350ef0093SJacques Vidrine  *
4*e71b7053SJung-uk Kim  * Licensed under the OpenSSL license (the "License").  You may not use
5*e71b7053SJung-uk Kim  * this file except in compliance with the License.  You can obtain a copy
6*e71b7053SJung-uk Kim  * in the file LICENSE in the source distribution or at
7*e71b7053SJung-uk Kim  * https://www.openssl.org/source/license.html
850ef0093SJacques Vidrine  */
974664626SKris Kennaway 
106f9291ceSJung-uk Kim /*
116f9291ceSJung-uk Kim  * Special method for a BIO where the other endpoint is also a BIO of this
126f9291ceSJung-uk Kim  * kind, handled by the same thread (i.e. the "peer" is actually ourselves,
136f9291ceSJung-uk Kim  * wearing a different hat). Such "BIO pairs" are mainly for using the SSL
146f9291ceSJung-uk Kim  * library with I/O interfaces for which no specific BIO method is available.
156f9291ceSJung-uk Kim  * See ssl/ssltest.c for some hints on how this can be used.
166f9291ceSJung-uk Kim  */
1774664626SKris Kennaway 
18*e71b7053SJung-uk Kim #include "e_os.h"
1974664626SKris Kennaway #include <assert.h>
20f579bf8eSKris Kennaway #include <limits.h>
2174664626SKris Kennaway #include <stdlib.h>
2274664626SKris Kennaway #include <string.h>
2374664626SKris Kennaway 
24*e71b7053SJung-uk Kim #include "bio_lcl.h"
2574664626SKris Kennaway #include <openssl/err.h>
2674664626SKris Kennaway #include <openssl/crypto.h>
2774664626SKris Kennaway 
2874664626SKris Kennaway static int bio_new(BIO *bio);
2974664626SKris Kennaway static int bio_free(BIO *bio);
3074664626SKris Kennaway static int bio_read(BIO *bio, char *buf, int size);
31ddd58736SKris Kennaway static int bio_write(BIO *bio, const char *buf, int num);
3274664626SKris Kennaway static long bio_ctrl(BIO *bio, int cmd, long num, void *ptr);
33ddd58736SKris Kennaway static int bio_puts(BIO *bio, const char *str);
3474664626SKris Kennaway 
3574664626SKris Kennaway static int bio_make_pair(BIO *bio1, BIO *bio2);
3674664626SKris Kennaway static void bio_destroy_pair(BIO *bio);
3774664626SKris Kennaway 
38*e71b7053SJung-uk Kim static const BIO_METHOD methods_biop = {
3974664626SKris Kennaway     BIO_TYPE_BIO,
4074664626SKris Kennaway     "BIO pair",
41*e71b7053SJung-uk Kim     /* TODO: Convert to new style write function */
42*e71b7053SJung-uk Kim     bwrite_conv,
4374664626SKris Kennaway     bio_write,
44*e71b7053SJung-uk Kim     /* TODO: Convert to new style read function */
45*e71b7053SJung-uk Kim     bread_conv,
4674664626SKris Kennaway     bio_read,
4774664626SKris Kennaway     bio_puts,
4874664626SKris Kennaway     NULL /* no bio_gets */ ,
4974664626SKris Kennaway     bio_ctrl,
5074664626SKris Kennaway     bio_new,
51f579bf8eSKris Kennaway     bio_free,
52f579bf8eSKris Kennaway     NULL                        /* no bio_callback_ctrl */
5374664626SKris Kennaway };
5474664626SKris Kennaway 
55*e71b7053SJung-uk Kim const BIO_METHOD *BIO_s_bio(void)
5674664626SKris Kennaway {
5774664626SKris Kennaway     return &methods_biop;
5874664626SKris Kennaway }
5974664626SKris Kennaway 
606f9291ceSJung-uk Kim struct bio_bio_st {
616f9291ceSJung-uk Kim     BIO *peer;                  /* NULL if buf == NULL. If peer != NULL, then
626f9291ceSJung-uk Kim                                  * peer->ptr is also a bio_bio_st, and its
636f9291ceSJung-uk Kim                                  * "peer" member points back to us. peer !=
646f9291ceSJung-uk Kim                                  * NULL iff init != 0 in the BIO. */
6574664626SKris Kennaway     /* This is for what we write (i.e. reading uses peer's struct): */
6674664626SKris Kennaway     int closed;                 /* valid iff peer != NULL */
6774664626SKris Kennaway     size_t len;                 /* valid iff buf != NULL; 0 if peer == NULL */
6874664626SKris Kennaway     size_t offset;              /* valid iff buf != NULL; 0 if len == 0 */
6974664626SKris Kennaway     size_t size;
7074664626SKris Kennaway     char *buf;                  /* "size" elements (if != NULL) */
7174664626SKris Kennaway     size_t request;             /* valid iff peer != NULL; 0 if len != 0,
7274664626SKris Kennaway                                  * otherwise set by peer to number of bytes
736f9291ceSJung-uk Kim                                  * it (unsuccessfully) tried to read, never
746f9291ceSJung-uk Kim                                  * more than buffer space (size-len)
756f9291ceSJung-uk Kim                                  * warrants. */
7674664626SKris Kennaway };
7774664626SKris Kennaway 
7874664626SKris Kennaway static int bio_new(BIO *bio)
7974664626SKris Kennaway {
80*e71b7053SJung-uk Kim     struct bio_bio_st *b = OPENSSL_zalloc(sizeof(*b));
8174664626SKris Kennaway 
8274664626SKris Kennaway     if (b == NULL)
8374664626SKris Kennaway         return 0;
8474664626SKris Kennaway 
856f9291ceSJung-uk Kim     /* enough for one TLS record (just a default) */
866f9291ceSJung-uk Kim     b->size = 17 * 1024;
8774664626SKris Kennaway 
8874664626SKris Kennaway     bio->ptr = b;
8974664626SKris Kennaway     return 1;
9074664626SKris Kennaway }
9174664626SKris Kennaway 
9274664626SKris Kennaway static int bio_free(BIO *bio)
9374664626SKris Kennaway {
9474664626SKris Kennaway     struct bio_bio_st *b;
9574664626SKris Kennaway 
9674664626SKris Kennaway     if (bio == NULL)
9774664626SKris Kennaway         return 0;
9874664626SKris Kennaway     b = bio->ptr;
9974664626SKris Kennaway 
10074664626SKris Kennaway     assert(b != NULL);
10174664626SKris Kennaway 
10274664626SKris Kennaway     if (b->peer)
10374664626SKris Kennaway         bio_destroy_pair(bio);
10474664626SKris Kennaway 
105ddd58736SKris Kennaway     OPENSSL_free(b->buf);
106ddd58736SKris Kennaway     OPENSSL_free(b);
10774664626SKris Kennaway 
10874664626SKris Kennaway     return 1;
10974664626SKris Kennaway }
11074664626SKris Kennaway 
11174664626SKris Kennaway static int bio_read(BIO *bio, char *buf, int size_)
11274664626SKris Kennaway {
11374664626SKris Kennaway     size_t size = size_;
11474664626SKris Kennaway     size_t rest;
11574664626SKris Kennaway     struct bio_bio_st *b, *peer_b;
11674664626SKris Kennaway 
11774664626SKris Kennaway     BIO_clear_retry_flags(bio);
11874664626SKris Kennaway 
11974664626SKris Kennaway     if (!bio->init)
12074664626SKris Kennaway         return 0;
12174664626SKris Kennaway 
12274664626SKris Kennaway     b = bio->ptr;
12374664626SKris Kennaway     assert(b != NULL);
12474664626SKris Kennaway     assert(b->peer != NULL);
12574664626SKris Kennaway     peer_b = b->peer->ptr;
12674664626SKris Kennaway     assert(peer_b != NULL);
12774664626SKris Kennaway     assert(peer_b->buf != NULL);
12874664626SKris Kennaway 
12974664626SKris Kennaway     peer_b->request = 0;        /* will be set in "retry_read" situation */
13074664626SKris Kennaway 
13174664626SKris Kennaway     if (buf == NULL || size == 0)
13274664626SKris Kennaway         return 0;
13374664626SKris Kennaway 
1346f9291ceSJung-uk Kim     if (peer_b->len == 0) {
13574664626SKris Kennaway         if (peer_b->closed)
13674664626SKris Kennaway             return 0;           /* writer has closed, and no data is left */
1376f9291ceSJung-uk Kim         else {
13874664626SKris Kennaway             BIO_set_retry_read(bio); /* buffer is empty */
13974664626SKris Kennaway             if (size <= peer_b->size)
14074664626SKris Kennaway                 peer_b->request = size;
14174664626SKris Kennaway             else
1426f9291ceSJung-uk Kim                 /*
1436f9291ceSJung-uk Kim                  * don't ask for more than the peer can deliver in one write
1446f9291ceSJung-uk Kim                  */
14574664626SKris Kennaway                 peer_b->request = peer_b->size;
14674664626SKris Kennaway             return -1;
14774664626SKris Kennaway         }
14874664626SKris Kennaway     }
14974664626SKris Kennaway 
15074664626SKris Kennaway     /* we can read */
15174664626SKris Kennaway     if (peer_b->len < size)
15274664626SKris Kennaway         size = peer_b->len;
15374664626SKris Kennaway 
15474664626SKris Kennaway     /* now read "size" bytes */
15574664626SKris Kennaway 
15674664626SKris Kennaway     rest = size;
15774664626SKris Kennaway 
15874664626SKris Kennaway     assert(rest > 0);
1596f9291ceSJung-uk Kim     do {                        /* one or two iterations */
16074664626SKris Kennaway         size_t chunk;
16174664626SKris Kennaway 
16274664626SKris Kennaway         assert(rest <= peer_b->len);
16374664626SKris Kennaway         if (peer_b->offset + rest <= peer_b->size)
16474664626SKris Kennaway             chunk = rest;
16574664626SKris Kennaway         else
16674664626SKris Kennaway             /* wrap around ring buffer */
16774664626SKris Kennaway             chunk = peer_b->size - peer_b->offset;
16874664626SKris Kennaway         assert(peer_b->offset + chunk <= peer_b->size);
16974664626SKris Kennaway 
17074664626SKris Kennaway         memcpy(buf, peer_b->buf + peer_b->offset, chunk);
17174664626SKris Kennaway 
17274664626SKris Kennaway         peer_b->len -= chunk;
1736f9291ceSJung-uk Kim         if (peer_b->len) {
17474664626SKris Kennaway             peer_b->offset += chunk;
17574664626SKris Kennaway             assert(peer_b->offset <= peer_b->size);
17674664626SKris Kennaway             if (peer_b->offset == peer_b->size)
17774664626SKris Kennaway                 peer_b->offset = 0;
17874664626SKris Kennaway             buf += chunk;
1796f9291ceSJung-uk Kim         } else {
18074664626SKris Kennaway             /* buffer now empty, no need to advance "buf" */
18174664626SKris Kennaway             assert(chunk == rest);
18274664626SKris Kennaway             peer_b->offset = 0;
18374664626SKris Kennaway         }
18474664626SKris Kennaway         rest -= chunk;
18574664626SKris Kennaway     }
18674664626SKris Kennaway     while (rest);
18774664626SKris Kennaway 
18874664626SKris Kennaway     return size;
18974664626SKris Kennaway }
19074664626SKris Kennaway 
1916f9291ceSJung-uk Kim /*-
1926f9291ceSJung-uk Kim  * non-copying interface: provide pointer to available data in buffer
193f579bf8eSKris Kennaway  *    bio_nread0:  return number of available bytes
194f579bf8eSKris Kennaway  *    bio_nread:   also advance index
195f579bf8eSKris Kennaway  * (example usage:  bio_nread0(), read from buffer, bio_nread()
196f579bf8eSKris Kennaway  *  or just         bio_nread(), read from buffer)
197f579bf8eSKris Kennaway  */
1986f9291ceSJung-uk Kim /*
1996f9291ceSJung-uk Kim  * WARNING: The non-copying interface is largely untested as of yet and may
2006f9291ceSJung-uk Kim  * contain bugs.
2016f9291ceSJung-uk Kim  */
2021f13597dSJung-uk Kim static ossl_ssize_t bio_nread0(BIO *bio, char **buf)
203f579bf8eSKris Kennaway {
204f579bf8eSKris Kennaway     struct bio_bio_st *b, *peer_b;
2051f13597dSJung-uk Kim     ossl_ssize_t num;
206f579bf8eSKris Kennaway 
207f579bf8eSKris Kennaway     BIO_clear_retry_flags(bio);
208f579bf8eSKris Kennaway 
209f579bf8eSKris Kennaway     if (!bio->init)
210f579bf8eSKris Kennaway         return 0;
211f579bf8eSKris Kennaway 
212f579bf8eSKris Kennaway     b = bio->ptr;
213f579bf8eSKris Kennaway     assert(b != NULL);
214f579bf8eSKris Kennaway     assert(b->peer != NULL);
215f579bf8eSKris Kennaway     peer_b = b->peer->ptr;
216f579bf8eSKris Kennaway     assert(peer_b != NULL);
217f579bf8eSKris Kennaway     assert(peer_b->buf != NULL);
218f579bf8eSKris Kennaway 
219f579bf8eSKris Kennaway     peer_b->request = 0;
220f579bf8eSKris Kennaway 
2216f9291ceSJung-uk Kim     if (peer_b->len == 0) {
222f579bf8eSKris Kennaway         char dummy;
223f579bf8eSKris Kennaway 
224f579bf8eSKris Kennaway         /* avoid code duplication -- nothing available for reading */
225f579bf8eSKris Kennaway         return bio_read(bio, &dummy, 1); /* returns 0 or -1 */
226f579bf8eSKris Kennaway     }
227f579bf8eSKris Kennaway 
228f579bf8eSKris Kennaway     num = peer_b->len;
229f579bf8eSKris Kennaway     if (peer_b->size < peer_b->offset + num)
230f579bf8eSKris Kennaway         /* no ring buffer wrap-around for non-copying interface */
231f579bf8eSKris Kennaway         num = peer_b->size - peer_b->offset;
232f579bf8eSKris Kennaway     assert(num > 0);
233f579bf8eSKris Kennaway 
234f579bf8eSKris Kennaway     if (buf != NULL)
235f579bf8eSKris Kennaway         *buf = peer_b->buf + peer_b->offset;
236f579bf8eSKris Kennaway     return num;
237f579bf8eSKris Kennaway }
238f579bf8eSKris Kennaway 
2391f13597dSJung-uk Kim static ossl_ssize_t bio_nread(BIO *bio, char **buf, size_t num_)
240f579bf8eSKris Kennaway {
241f579bf8eSKris Kennaway     struct bio_bio_st *b, *peer_b;
2421f13597dSJung-uk Kim     ossl_ssize_t num, available;
243f579bf8eSKris Kennaway 
244*e71b7053SJung-uk Kim     if (num_ > OSSL_SSIZE_MAX)
245*e71b7053SJung-uk Kim         num = OSSL_SSIZE_MAX;
246f579bf8eSKris Kennaway     else
2471f13597dSJung-uk Kim         num = (ossl_ssize_t) num_;
248f579bf8eSKris Kennaway 
249f579bf8eSKris Kennaway     available = bio_nread0(bio, buf);
250f579bf8eSKris Kennaway     if (num > available)
251f579bf8eSKris Kennaway         num = available;
252f579bf8eSKris Kennaway     if (num <= 0)
253f579bf8eSKris Kennaway         return num;
254f579bf8eSKris Kennaway 
255f579bf8eSKris Kennaway     b = bio->ptr;
256f579bf8eSKris Kennaway     peer_b = b->peer->ptr;
257f579bf8eSKris Kennaway 
258f579bf8eSKris Kennaway     peer_b->len -= num;
2596f9291ceSJung-uk Kim     if (peer_b->len) {
260f579bf8eSKris Kennaway         peer_b->offset += num;
261f579bf8eSKris Kennaway         assert(peer_b->offset <= peer_b->size);
262f579bf8eSKris Kennaway         if (peer_b->offset == peer_b->size)
263f579bf8eSKris Kennaway             peer_b->offset = 0;
2646f9291ceSJung-uk Kim     } else
265f579bf8eSKris Kennaway         peer_b->offset = 0;
266f579bf8eSKris Kennaway 
267f579bf8eSKris Kennaway     return num;
268f579bf8eSKris Kennaway }
269f579bf8eSKris Kennaway 
270ddd58736SKris Kennaway static int bio_write(BIO *bio, const char *buf, int num_)
27174664626SKris Kennaway {
27274664626SKris Kennaway     size_t num = num_;
27374664626SKris Kennaway     size_t rest;
27474664626SKris Kennaway     struct bio_bio_st *b;
27574664626SKris Kennaway 
27674664626SKris Kennaway     BIO_clear_retry_flags(bio);
27774664626SKris Kennaway 
27874664626SKris Kennaway     if (!bio->init || buf == NULL || num == 0)
27974664626SKris Kennaway         return 0;
28074664626SKris Kennaway 
28174664626SKris Kennaway     b = bio->ptr;
28274664626SKris Kennaway     assert(b != NULL);
28374664626SKris Kennaway     assert(b->peer != NULL);
28474664626SKris Kennaway     assert(b->buf != NULL);
28574664626SKris Kennaway 
28674664626SKris Kennaway     b->request = 0;
2876f9291ceSJung-uk Kim     if (b->closed) {
28874664626SKris Kennaway         /* we already closed */
28974664626SKris Kennaway         BIOerr(BIO_F_BIO_WRITE, BIO_R_BROKEN_PIPE);
29074664626SKris Kennaway         return -1;
29174664626SKris Kennaway     }
29274664626SKris Kennaway 
29374664626SKris Kennaway     assert(b->len <= b->size);
29474664626SKris Kennaway 
2956f9291ceSJung-uk Kim     if (b->len == b->size) {
29674664626SKris Kennaway         BIO_set_retry_write(bio); /* buffer is full */
29774664626SKris Kennaway         return -1;
29874664626SKris Kennaway     }
29974664626SKris Kennaway 
30074664626SKris Kennaway     /* we can write */
30174664626SKris Kennaway     if (num > b->size - b->len)
30274664626SKris Kennaway         num = b->size - b->len;
30374664626SKris Kennaway 
30474664626SKris Kennaway     /* now write "num" bytes */
30574664626SKris Kennaway 
30674664626SKris Kennaway     rest = num;
30774664626SKris Kennaway 
30874664626SKris Kennaway     assert(rest > 0);
3096f9291ceSJung-uk Kim     do {                        /* one or two iterations */
31074664626SKris Kennaway         size_t write_offset;
31174664626SKris Kennaway         size_t chunk;
31274664626SKris Kennaway 
31374664626SKris Kennaway         assert(b->len + rest <= b->size);
31474664626SKris Kennaway 
31574664626SKris Kennaway         write_offset = b->offset + b->len;
31674664626SKris Kennaway         if (write_offset >= b->size)
31774664626SKris Kennaway             write_offset -= b->size;
31874664626SKris Kennaway         /* b->buf[write_offset] is the first byte we can write to. */
31974664626SKris Kennaway 
32074664626SKris Kennaway         if (write_offset + rest <= b->size)
32174664626SKris Kennaway             chunk = rest;
32274664626SKris Kennaway         else
32374664626SKris Kennaway             /* wrap around ring buffer */
32474664626SKris Kennaway             chunk = b->size - write_offset;
32574664626SKris Kennaway 
32674664626SKris Kennaway         memcpy(b->buf + write_offset, buf, chunk);
32774664626SKris Kennaway 
32874664626SKris Kennaway         b->len += chunk;
32974664626SKris Kennaway 
33074664626SKris Kennaway         assert(b->len <= b->size);
33174664626SKris Kennaway 
33274664626SKris Kennaway         rest -= chunk;
33374664626SKris Kennaway         buf += chunk;
33474664626SKris Kennaway     }
33574664626SKris Kennaway     while (rest);
33674664626SKris Kennaway 
33774664626SKris Kennaway     return num;
33874664626SKris Kennaway }
33974664626SKris Kennaway 
3406f9291ceSJung-uk Kim /*-
3416f9291ceSJung-uk Kim  * non-copying interface: provide pointer to region to write to
342f579bf8eSKris Kennaway  *   bio_nwrite0:  check how much space is available
343f579bf8eSKris Kennaway  *   bio_nwrite:   also increase length
344f579bf8eSKris Kennaway  * (example usage:  bio_nwrite0(), write to buffer, bio_nwrite()
345f579bf8eSKris Kennaway  *  or just         bio_nwrite(), write to buffer)
346f579bf8eSKris Kennaway  */
3471f13597dSJung-uk Kim static ossl_ssize_t bio_nwrite0(BIO *bio, char **buf)
348f579bf8eSKris Kennaway {
349f579bf8eSKris Kennaway     struct bio_bio_st *b;
350f579bf8eSKris Kennaway     size_t num;
351f579bf8eSKris Kennaway     size_t write_offset;
352f579bf8eSKris Kennaway 
353f579bf8eSKris Kennaway     BIO_clear_retry_flags(bio);
354f579bf8eSKris Kennaway 
355f579bf8eSKris Kennaway     if (!bio->init)
356f579bf8eSKris Kennaway         return 0;
357f579bf8eSKris Kennaway 
358f579bf8eSKris Kennaway     b = bio->ptr;
359f579bf8eSKris Kennaway     assert(b != NULL);
360f579bf8eSKris Kennaway     assert(b->peer != NULL);
361f579bf8eSKris Kennaway     assert(b->buf != NULL);
362f579bf8eSKris Kennaway 
363f579bf8eSKris Kennaway     b->request = 0;
3646f9291ceSJung-uk Kim     if (b->closed) {
365f579bf8eSKris Kennaway         BIOerr(BIO_F_BIO_NWRITE0, BIO_R_BROKEN_PIPE);
366f579bf8eSKris Kennaway         return -1;
367f579bf8eSKris Kennaway     }
368f579bf8eSKris Kennaway 
369f579bf8eSKris Kennaway     assert(b->len <= b->size);
370f579bf8eSKris Kennaway 
3716f9291ceSJung-uk Kim     if (b->len == b->size) {
372f579bf8eSKris Kennaway         BIO_set_retry_write(bio);
373f579bf8eSKris Kennaway         return -1;
374f579bf8eSKris Kennaway     }
375f579bf8eSKris Kennaway 
376f579bf8eSKris Kennaway     num = b->size - b->len;
377f579bf8eSKris Kennaway     write_offset = b->offset + b->len;
378f579bf8eSKris Kennaway     if (write_offset >= b->size)
379f579bf8eSKris Kennaway         write_offset -= b->size;
380f579bf8eSKris Kennaway     if (write_offset + num > b->size)
3816f9291ceSJung-uk Kim         /*
3826f9291ceSJung-uk Kim          * no ring buffer wrap-around for non-copying interface (to fulfil
3836f9291ceSJung-uk Kim          * the promise by BIO_ctrl_get_write_guarantee, BIO_nwrite may have
3846f9291ceSJung-uk Kim          * to be called twice)
3856f9291ceSJung-uk Kim          */
386f579bf8eSKris Kennaway         num = b->size - write_offset;
387f579bf8eSKris Kennaway 
388f579bf8eSKris Kennaway     if (buf != NULL)
389f579bf8eSKris Kennaway         *buf = b->buf + write_offset;
390f579bf8eSKris Kennaway     assert(write_offset + num <= b->size);
391f579bf8eSKris Kennaway 
392f579bf8eSKris Kennaway     return num;
393f579bf8eSKris Kennaway }
394f579bf8eSKris Kennaway 
3951f13597dSJung-uk Kim static ossl_ssize_t bio_nwrite(BIO *bio, char **buf, size_t num_)
396f579bf8eSKris Kennaway {
397f579bf8eSKris Kennaway     struct bio_bio_st *b;
3981f13597dSJung-uk Kim     ossl_ssize_t num, space;
399f579bf8eSKris Kennaway 
400*e71b7053SJung-uk Kim     if (num_ > OSSL_SSIZE_MAX)
401*e71b7053SJung-uk Kim         num = OSSL_SSIZE_MAX;
402f579bf8eSKris Kennaway     else
4031f13597dSJung-uk Kim         num = (ossl_ssize_t) num_;
404f579bf8eSKris Kennaway 
405f579bf8eSKris Kennaway     space = bio_nwrite0(bio, buf);
406f579bf8eSKris Kennaway     if (num > space)
407f579bf8eSKris Kennaway         num = space;
408f579bf8eSKris Kennaway     if (num <= 0)
409f579bf8eSKris Kennaway         return num;
410f579bf8eSKris Kennaway     b = bio->ptr;
411f579bf8eSKris Kennaway     assert(b != NULL);
412f579bf8eSKris Kennaway     b->len += num;
413f579bf8eSKris Kennaway     assert(b->len <= b->size);
414f579bf8eSKris Kennaway 
415f579bf8eSKris Kennaway     return num;
416f579bf8eSKris Kennaway }
417f579bf8eSKris Kennaway 
41874664626SKris Kennaway static long bio_ctrl(BIO *bio, int cmd, long num, void *ptr)
41974664626SKris Kennaway {
42074664626SKris Kennaway     long ret;
42174664626SKris Kennaway     struct bio_bio_st *b = bio->ptr;
42274664626SKris Kennaway 
42374664626SKris Kennaway     assert(b != NULL);
42474664626SKris Kennaway 
4256f9291ceSJung-uk Kim     switch (cmd) {
42674664626SKris Kennaway         /* specific CTRL codes */
42774664626SKris Kennaway 
42874664626SKris Kennaway     case BIO_C_SET_WRITE_BUF_SIZE:
4296f9291ceSJung-uk Kim         if (b->peer) {
43074664626SKris Kennaway             BIOerr(BIO_F_BIO_CTRL, BIO_R_IN_USE);
43174664626SKris Kennaway             ret = 0;
4326f9291ceSJung-uk Kim         } else if (num == 0) {
43374664626SKris Kennaway             BIOerr(BIO_F_BIO_CTRL, BIO_R_INVALID_ARGUMENT);
43474664626SKris Kennaway             ret = 0;
4356f9291ceSJung-uk Kim         } else {
43674664626SKris Kennaway             size_t new_size = num;
43774664626SKris Kennaway 
4386f9291ceSJung-uk Kim             if (b->size != new_size) {
439ddd58736SKris Kennaway                 OPENSSL_free(b->buf);
44074664626SKris Kennaway                 b->buf = NULL;
44174664626SKris Kennaway                 b->size = new_size;
44274664626SKris Kennaway             }
44374664626SKris Kennaway             ret = 1;
44474664626SKris Kennaway         }
44574664626SKris Kennaway         break;
44674664626SKris Kennaway 
44774664626SKris Kennaway     case BIO_C_GET_WRITE_BUF_SIZE:
448a21b1b38SKris Kennaway         ret = (long)b->size;
449a21b1b38SKris Kennaway         break;
45074664626SKris Kennaway 
45174664626SKris Kennaway     case BIO_C_MAKE_BIO_PAIR:
45274664626SKris Kennaway         {
45374664626SKris Kennaway             BIO *other_bio = ptr;
45474664626SKris Kennaway 
45574664626SKris Kennaway             if (bio_make_pair(bio, other_bio))
45674664626SKris Kennaway                 ret = 1;
45774664626SKris Kennaway             else
45874664626SKris Kennaway                 ret = 0;
45974664626SKris Kennaway         }
46074664626SKris Kennaway         break;
46174664626SKris Kennaway 
46274664626SKris Kennaway     case BIO_C_DESTROY_BIO_PAIR:
4636f9291ceSJung-uk Kim         /*
4646f9291ceSJung-uk Kim          * Affects both BIOs in the pair -- call just once! Or let
4656f9291ceSJung-uk Kim          * BIO_free(bio1); BIO_free(bio2); do the job.
4666f9291ceSJung-uk Kim          */
46774664626SKris Kennaway         bio_destroy_pair(bio);
46874664626SKris Kennaway         ret = 1;
46974664626SKris Kennaway         break;
47074664626SKris Kennaway 
47174664626SKris Kennaway     case BIO_C_GET_WRITE_GUARANTEE:
4726f9291ceSJung-uk Kim         /*
4736f9291ceSJung-uk Kim          * How many bytes can the caller feed to the next write without
4746f9291ceSJung-uk Kim          * having to keep any?
4756f9291ceSJung-uk Kim          */
47674664626SKris Kennaway         if (b->peer == NULL || b->closed)
47774664626SKris Kennaway             ret = 0;
47874664626SKris Kennaway         else
47974664626SKris Kennaway             ret = (long)b->size - b->len;
48074664626SKris Kennaway         break;
48174664626SKris Kennaway 
48274664626SKris Kennaway     case BIO_C_GET_READ_REQUEST:
4836f9291ceSJung-uk Kim         /*
4846f9291ceSJung-uk Kim          * If the peer unsuccessfully tried to read, how many bytes were
4856f9291ceSJung-uk Kim          * requested? (As with BIO_CTRL_PENDING, that number can usually be
4866f9291ceSJung-uk Kim          * treated as boolean.)
4876f9291ceSJung-uk Kim          */
48874664626SKris Kennaway         ret = (long)b->request;
48974664626SKris Kennaway         break;
49074664626SKris Kennaway 
491f579bf8eSKris Kennaway     case BIO_C_RESET_READ_REQUEST:
4926f9291ceSJung-uk Kim         /*
4936f9291ceSJung-uk Kim          * Reset request.  (Can be useful after read attempts at the other
4946f9291ceSJung-uk Kim          * side that are meant to be non-blocking, e.g. when probing SSL_read
4956f9291ceSJung-uk Kim          * to see if any data is available.)
4966f9291ceSJung-uk Kim          */
497f579bf8eSKris Kennaway         b->request = 0;
498f579bf8eSKris Kennaway         ret = 1;
499f579bf8eSKris Kennaway         break;
500f579bf8eSKris Kennaway 
50174664626SKris Kennaway     case BIO_C_SHUTDOWN_WR:
50274664626SKris Kennaway         /* similar to shutdown(..., SHUT_WR) */
50374664626SKris Kennaway         b->closed = 1;
50474664626SKris Kennaway         ret = 1;
50574664626SKris Kennaway         break;
50674664626SKris Kennaway 
507f579bf8eSKris Kennaway     case BIO_C_NREAD0:
508f579bf8eSKris Kennaway         /* prepare for non-copying read */
509f579bf8eSKris Kennaway         ret = (long)bio_nread0(bio, ptr);
510f579bf8eSKris Kennaway         break;
511f579bf8eSKris Kennaway 
512f579bf8eSKris Kennaway     case BIO_C_NREAD:
513f579bf8eSKris Kennaway         /* non-copying read */
514f579bf8eSKris Kennaway         ret = (long)bio_nread(bio, ptr, (size_t)num);
515f579bf8eSKris Kennaway         break;
516f579bf8eSKris Kennaway 
517f579bf8eSKris Kennaway     case BIO_C_NWRITE0:
518f579bf8eSKris Kennaway         /* prepare for non-copying write */
519f579bf8eSKris Kennaway         ret = (long)bio_nwrite0(bio, ptr);
520f579bf8eSKris Kennaway         break;
521f579bf8eSKris Kennaway 
522f579bf8eSKris Kennaway     case BIO_C_NWRITE:
523f579bf8eSKris Kennaway         /* non-copying write */
524f579bf8eSKris Kennaway         ret = (long)bio_nwrite(bio, ptr, (size_t)num);
525f579bf8eSKris Kennaway         break;
526f579bf8eSKris Kennaway 
52774664626SKris Kennaway         /* standard CTRL codes follow */
52874664626SKris Kennaway 
52974664626SKris Kennaway     case BIO_CTRL_RESET:
5306f9291ceSJung-uk Kim         if (b->buf != NULL) {
53174664626SKris Kennaway             b->len = 0;
53274664626SKris Kennaway             b->offset = 0;
53374664626SKris Kennaway         }
53474664626SKris Kennaway         ret = 0;
53574664626SKris Kennaway         break;
53674664626SKris Kennaway 
53774664626SKris Kennaway     case BIO_CTRL_GET_CLOSE:
53874664626SKris Kennaway         ret = bio->shutdown;
53974664626SKris Kennaway         break;
54074664626SKris Kennaway 
54174664626SKris Kennaway     case BIO_CTRL_SET_CLOSE:
54274664626SKris Kennaway         bio->shutdown = (int)num;
54374664626SKris Kennaway         ret = 1;
54474664626SKris Kennaway         break;
54574664626SKris Kennaway 
54674664626SKris Kennaway     case BIO_CTRL_PENDING:
5476f9291ceSJung-uk Kim         if (b->peer != NULL) {
54874664626SKris Kennaway             struct bio_bio_st *peer_b = b->peer->ptr;
54974664626SKris Kennaway 
55074664626SKris Kennaway             ret = (long)peer_b->len;
5516f9291ceSJung-uk Kim         } else
55274664626SKris Kennaway             ret = 0;
55374664626SKris Kennaway         break;
55474664626SKris Kennaway 
55574664626SKris Kennaway     case BIO_CTRL_WPENDING:
55674664626SKris Kennaway         if (b->buf != NULL)
55774664626SKris Kennaway             ret = (long)b->len;
55874664626SKris Kennaway         else
55974664626SKris Kennaway             ret = 0;
56074664626SKris Kennaway         break;
56174664626SKris Kennaway 
56274664626SKris Kennaway     case BIO_CTRL_DUP:
56374664626SKris Kennaway         /* See BIO_dup_chain for circumstances we have to expect. */
56474664626SKris Kennaway         {
56574664626SKris Kennaway             BIO *other_bio = ptr;
56674664626SKris Kennaway             struct bio_bio_st *other_b;
56774664626SKris Kennaway 
56874664626SKris Kennaway             assert(other_bio != NULL);
56974664626SKris Kennaway             other_b = other_bio->ptr;
57074664626SKris Kennaway             assert(other_b != NULL);
57174664626SKris Kennaway 
57274664626SKris Kennaway             assert(other_b->buf == NULL); /* other_bio is always fresh */
57374664626SKris Kennaway 
57474664626SKris Kennaway             other_b->size = b->size;
57574664626SKris Kennaway         }
57674664626SKris Kennaway 
57774664626SKris Kennaway         ret = 1;
57874664626SKris Kennaway         break;
57974664626SKris Kennaway 
58074664626SKris Kennaway     case BIO_CTRL_FLUSH:
58174664626SKris Kennaway         ret = 1;
58274664626SKris Kennaway         break;
58374664626SKris Kennaway 
58474664626SKris Kennaway     case BIO_CTRL_EOF:
585aeb5019cSJung-uk Kim         if (b->peer != NULL) {
586aeb5019cSJung-uk Kim             struct bio_bio_st *peer_b = b->peer->ptr;
58774664626SKris Kennaway 
588aeb5019cSJung-uk Kim             if (peer_b->len == 0 && peer_b->closed)
589aeb5019cSJung-uk Kim                 ret = 1;
590aeb5019cSJung-uk Kim             else
591aeb5019cSJung-uk Kim                 ret = 0;
592aeb5019cSJung-uk Kim         } else {
59374664626SKris Kennaway             ret = 1;
59474664626SKris Kennaway         }
59574664626SKris Kennaway         break;
59674664626SKris Kennaway 
59774664626SKris Kennaway     default:
59874664626SKris Kennaway         ret = 0;
59974664626SKris Kennaway     }
60074664626SKris Kennaway     return ret;
60174664626SKris Kennaway }
60274664626SKris Kennaway 
603ddd58736SKris Kennaway static int bio_puts(BIO *bio, const char *str)
60474664626SKris Kennaway {
60574664626SKris Kennaway     return bio_write(bio, str, strlen(str));
60674664626SKris Kennaway }
60774664626SKris Kennaway 
60874664626SKris Kennaway static int bio_make_pair(BIO *bio1, BIO *bio2)
60974664626SKris Kennaway {
61074664626SKris Kennaway     struct bio_bio_st *b1, *b2;
61174664626SKris Kennaway 
61274664626SKris Kennaway     assert(bio1 != NULL);
61374664626SKris Kennaway     assert(bio2 != NULL);
61474664626SKris Kennaway 
61574664626SKris Kennaway     b1 = bio1->ptr;
61674664626SKris Kennaway     b2 = bio2->ptr;
61774664626SKris Kennaway 
6186f9291ceSJung-uk Kim     if (b1->peer != NULL || b2->peer != NULL) {
61974664626SKris Kennaway         BIOerr(BIO_F_BIO_MAKE_PAIR, BIO_R_IN_USE);
62074664626SKris Kennaway         return 0;
62174664626SKris Kennaway     }
62274664626SKris Kennaway 
6236f9291ceSJung-uk Kim     if (b1->buf == NULL) {
624ddd58736SKris Kennaway         b1->buf = OPENSSL_malloc(b1->size);
6256f9291ceSJung-uk Kim         if (b1->buf == NULL) {
62674664626SKris Kennaway             BIOerr(BIO_F_BIO_MAKE_PAIR, ERR_R_MALLOC_FAILURE);
62774664626SKris Kennaway             return 0;
62874664626SKris Kennaway         }
62974664626SKris Kennaway         b1->len = 0;
63074664626SKris Kennaway         b1->offset = 0;
63174664626SKris Kennaway     }
63274664626SKris Kennaway 
6336f9291ceSJung-uk Kim     if (b2->buf == NULL) {
634ddd58736SKris Kennaway         b2->buf = OPENSSL_malloc(b2->size);
6356f9291ceSJung-uk Kim         if (b2->buf == NULL) {
63674664626SKris Kennaway             BIOerr(BIO_F_BIO_MAKE_PAIR, ERR_R_MALLOC_FAILURE);
63774664626SKris Kennaway             return 0;
63874664626SKris Kennaway         }
63974664626SKris Kennaway         b2->len = 0;
64074664626SKris Kennaway         b2->offset = 0;
64174664626SKris Kennaway     }
64274664626SKris Kennaway 
64374664626SKris Kennaway     b1->peer = bio2;
64474664626SKris Kennaway     b1->closed = 0;
64574664626SKris Kennaway     b1->request = 0;
64674664626SKris Kennaway     b2->peer = bio1;
64774664626SKris Kennaway     b2->closed = 0;
64874664626SKris Kennaway     b2->request = 0;
64974664626SKris Kennaway 
65074664626SKris Kennaway     bio1->init = 1;
65174664626SKris Kennaway     bio2->init = 1;
65274664626SKris Kennaway 
65374664626SKris Kennaway     return 1;
65474664626SKris Kennaway }
65574664626SKris Kennaway 
65674664626SKris Kennaway static void bio_destroy_pair(BIO *bio)
65774664626SKris Kennaway {
65874664626SKris Kennaway     struct bio_bio_st *b = bio->ptr;
65974664626SKris Kennaway 
6606f9291ceSJung-uk Kim     if (b != NULL) {
66174664626SKris Kennaway         BIO *peer_bio = b->peer;
66274664626SKris Kennaway 
6636f9291ceSJung-uk Kim         if (peer_bio != NULL) {
66474664626SKris Kennaway             struct bio_bio_st *peer_b = peer_bio->ptr;
66574664626SKris Kennaway 
66674664626SKris Kennaway             assert(peer_b != NULL);
66774664626SKris Kennaway             assert(peer_b->peer == bio);
66874664626SKris Kennaway 
66974664626SKris Kennaway             peer_b->peer = NULL;
67074664626SKris Kennaway             peer_bio->init = 0;
67174664626SKris Kennaway             assert(peer_b->buf != NULL);
67274664626SKris Kennaway             peer_b->len = 0;
67374664626SKris Kennaway             peer_b->offset = 0;
67474664626SKris Kennaway 
67574664626SKris Kennaway             b->peer = NULL;
67674664626SKris Kennaway             bio->init = 0;
67774664626SKris Kennaway             assert(b->buf != NULL);
67874664626SKris Kennaway             b->len = 0;
67974664626SKris Kennaway             b->offset = 0;
68074664626SKris Kennaway         }
68174664626SKris Kennaway     }
68274664626SKris Kennaway }
68374664626SKris Kennaway 
68474664626SKris Kennaway /* Exported convenience functions */
68574664626SKris Kennaway int BIO_new_bio_pair(BIO **bio1_p, size_t writebuf1,
68674664626SKris Kennaway                      BIO **bio2_p, size_t writebuf2)
68774664626SKris Kennaway {
68874664626SKris Kennaway     BIO *bio1 = NULL, *bio2 = NULL;
68974664626SKris Kennaway     long r;
69074664626SKris Kennaway     int ret = 0;
69174664626SKris Kennaway 
69274664626SKris Kennaway     bio1 = BIO_new(BIO_s_bio());
69374664626SKris Kennaway     if (bio1 == NULL)
69474664626SKris Kennaway         goto err;
69574664626SKris Kennaway     bio2 = BIO_new(BIO_s_bio());
69674664626SKris Kennaway     if (bio2 == NULL)
69774664626SKris Kennaway         goto err;
69874664626SKris Kennaway 
6996f9291ceSJung-uk Kim     if (writebuf1) {
70074664626SKris Kennaway         r = BIO_set_write_buf_size(bio1, writebuf1);
70174664626SKris Kennaway         if (!r)
70274664626SKris Kennaway             goto err;
70374664626SKris Kennaway     }
7046f9291ceSJung-uk Kim     if (writebuf2) {
70574664626SKris Kennaway         r = BIO_set_write_buf_size(bio2, writebuf2);
70674664626SKris Kennaway         if (!r)
70774664626SKris Kennaway             goto err;
70874664626SKris Kennaway     }
70974664626SKris Kennaway 
71074664626SKris Kennaway     r = BIO_make_bio_pair(bio1, bio2);
71174664626SKris Kennaway     if (!r)
71274664626SKris Kennaway         goto err;
71374664626SKris Kennaway     ret = 1;
71474664626SKris Kennaway 
71574664626SKris Kennaway  err:
7166f9291ceSJung-uk Kim     if (ret == 0) {
71774664626SKris Kennaway         BIO_free(bio1);
71874664626SKris Kennaway         bio1 = NULL;
71974664626SKris Kennaway         BIO_free(bio2);
72074664626SKris Kennaway         bio2 = NULL;
72174664626SKris Kennaway     }
72274664626SKris Kennaway 
72374664626SKris Kennaway     *bio1_p = bio1;
72474664626SKris Kennaway     *bio2_p = bio2;
72574664626SKris Kennaway     return ret;
72674664626SKris Kennaway }
72774664626SKris Kennaway 
72874664626SKris Kennaway size_t BIO_ctrl_get_write_guarantee(BIO *bio)
72974664626SKris Kennaway {
73074664626SKris Kennaway     return BIO_ctrl(bio, BIO_C_GET_WRITE_GUARANTEE, 0, NULL);
73174664626SKris Kennaway }
73274664626SKris Kennaway 
73374664626SKris Kennaway size_t BIO_ctrl_get_read_request(BIO *bio)
73474664626SKris Kennaway {
73574664626SKris Kennaway     return BIO_ctrl(bio, BIO_C_GET_READ_REQUEST, 0, NULL);
73674664626SKris Kennaway }
737f579bf8eSKris Kennaway 
738f579bf8eSKris Kennaway int BIO_ctrl_reset_read_request(BIO *bio)
739f579bf8eSKris Kennaway {
740f579bf8eSKris Kennaway     return (BIO_ctrl(bio, BIO_C_RESET_READ_REQUEST, 0, NULL) != 0);
741f579bf8eSKris Kennaway }
742f579bf8eSKris Kennaway 
7436f9291ceSJung-uk Kim /*
7446f9291ceSJung-uk Kim  * BIO_nread0/nread/nwrite0/nwrite are available only for BIO pairs for now
7456f9291ceSJung-uk Kim  * (conceivably some other BIOs could allow non-copying reads and writes
7466f9291ceSJung-uk Kim  * too.)
747f579bf8eSKris Kennaway  */
748f579bf8eSKris Kennaway int BIO_nread0(BIO *bio, char **buf)
749f579bf8eSKris Kennaway {
750f579bf8eSKris Kennaway     long ret;
751f579bf8eSKris Kennaway 
7526f9291ceSJung-uk Kim     if (!bio->init) {
753f579bf8eSKris Kennaway         BIOerr(BIO_F_BIO_NREAD0, BIO_R_UNINITIALIZED);
754f579bf8eSKris Kennaway         return -2;
755f579bf8eSKris Kennaway     }
756f579bf8eSKris Kennaway 
757f579bf8eSKris Kennaway     ret = BIO_ctrl(bio, BIO_C_NREAD0, 0, buf);
758f579bf8eSKris Kennaway     if (ret > INT_MAX)
759f579bf8eSKris Kennaway         return INT_MAX;
760f579bf8eSKris Kennaway     else
761f579bf8eSKris Kennaway         return (int)ret;
762f579bf8eSKris Kennaway }
763f579bf8eSKris Kennaway 
764f579bf8eSKris Kennaway int BIO_nread(BIO *bio, char **buf, int num)
765f579bf8eSKris Kennaway {
766f579bf8eSKris Kennaway     int ret;
767f579bf8eSKris Kennaway 
7686f9291ceSJung-uk Kim     if (!bio->init) {
769f579bf8eSKris Kennaway         BIOerr(BIO_F_BIO_NREAD, BIO_R_UNINITIALIZED);
770f579bf8eSKris Kennaway         return -2;
771f579bf8eSKris Kennaway     }
772f579bf8eSKris Kennaway 
773f579bf8eSKris Kennaway     ret = (int)BIO_ctrl(bio, BIO_C_NREAD, num, buf);
774f579bf8eSKris Kennaway     if (ret > 0)
775f579bf8eSKris Kennaway         bio->num_read += ret;
776f579bf8eSKris Kennaway     return ret;
777f579bf8eSKris Kennaway }
778f579bf8eSKris Kennaway 
779f579bf8eSKris Kennaway int BIO_nwrite0(BIO *bio, char **buf)
780f579bf8eSKris Kennaway {
781f579bf8eSKris Kennaway     long ret;
782f579bf8eSKris Kennaway 
7836f9291ceSJung-uk Kim     if (!bio->init) {
784f579bf8eSKris Kennaway         BIOerr(BIO_F_BIO_NWRITE0, BIO_R_UNINITIALIZED);
785f579bf8eSKris Kennaway         return -2;
786f579bf8eSKris Kennaway     }
787f579bf8eSKris Kennaway 
788f579bf8eSKris Kennaway     ret = BIO_ctrl(bio, BIO_C_NWRITE0, 0, buf);
789f579bf8eSKris Kennaway     if (ret > INT_MAX)
790f579bf8eSKris Kennaway         return INT_MAX;
791f579bf8eSKris Kennaway     else
792f579bf8eSKris Kennaway         return (int)ret;
793f579bf8eSKris Kennaway }
794f579bf8eSKris Kennaway 
795f579bf8eSKris Kennaway int BIO_nwrite(BIO *bio, char **buf, int num)
796f579bf8eSKris Kennaway {
797f579bf8eSKris Kennaway     int ret;
798f579bf8eSKris Kennaway 
7996f9291ceSJung-uk Kim     if (!bio->init) {
800f579bf8eSKris Kennaway         BIOerr(BIO_F_BIO_NWRITE, BIO_R_UNINITIALIZED);
801f579bf8eSKris Kennaway         return -2;
802f579bf8eSKris Kennaway     }
803f579bf8eSKris Kennaway 
804f579bf8eSKris Kennaway     ret = BIO_ctrl(bio, BIO_C_NWRITE, num, buf);
805f579bf8eSKris Kennaway     if (ret > 0)
806db522d3aSSimon L. B. Nielsen         bio->num_write += ret;
807f579bf8eSKris Kennaway     return ret;
808f579bf8eSKris Kennaway }
809