blob: 9eae290f1a3bbc2bf1b55bcc4a5676e41f635860 [file] [log] [blame]
David Benjamin43ec06f2014-08-05 02:28:57 -04001/* Copyright (c) 2014, Google Inc.
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
15#include "async_bio.h"
16
17#include <errno.h>
Adam Langley2b2d66d2015-01-30 17:08:37 -080018#include <string.h>
19
Matt Braithwaited17d74d2016-08-17 20:10:28 -070020#include <openssl/bio.h>
David Benjamin43ec06f2014-08-05 02:28:57 -040021#include <openssl/mem.h>
22
David Benjamin17cf2cb2016-12-13 01:07:13 -050023#include "../../crypto/internal.h"
24
Adam Langley2b2d66d2015-01-30 17:08:37 -080025
David Benjamin43ec06f2014-08-05 02:28:57 -040026namespace {
27
David Benjaminc273d2c2015-02-09 12:59:46 -050028extern const BIO_METHOD g_async_bio_method;
David Benjamin43ec06f2014-08-05 02:28:57 -040029
David Benjaminc273d2c2015-02-09 12:59:46 -050030struct AsyncBio {
David Benjamin6fd297b2014-08-11 18:43:38 -040031 bool datagram;
David Benjamin13e81fc2015-11-02 17:16:13 -050032 bool enforce_write_quota;
David Benjamin43ec06f2014-08-05 02:28:57 -040033 size_t read_quota;
34 size_t write_quota;
35};
36
David Benjaminc273d2c2015-02-09 12:59:46 -050037AsyncBio *GetData(BIO *bio) {
38 if (bio->method != &g_async_bio_method) {
David Benjamin43ec06f2014-08-05 02:28:57 -040039 return NULL;
40 }
David Benjaminc273d2c2015-02-09 12:59:46 -050041 return (AsyncBio *)bio->ptr;
David Benjamin43ec06f2014-08-05 02:28:57 -040042}
43
David Benjaminc273d2c2015-02-09 12:59:46 -050044static int AsyncWrite(BIO *bio, const char *in, int inl) {
45 AsyncBio *a = GetData(bio);
David Benjamin43ec06f2014-08-05 02:28:57 -040046 if (a == NULL || bio->next_bio == NULL) {
47 return 0;
48 }
49
David Benjamin13e81fc2015-11-02 17:16:13 -050050 if (!a->enforce_write_quota) {
David Benjamin6fd297b2014-08-11 18:43:38 -040051 return BIO_write(bio->next_bio, in, inl);
52 }
53
David Benjamin43ec06f2014-08-05 02:28:57 -040054 BIO_clear_retry_flags(bio);
55
56 if (a->write_quota == 0) {
57 BIO_set_retry_write(bio);
58 errno = EAGAIN;
59 return -1;
60 }
61
David Benjamin7e560512023-05-23 11:45:11 -040062 if (!a->datagram && static_cast<size_t>(inl) > a->write_quota) {
63 inl = static_cast<int>(a->write_quota);
David Benjamin43ec06f2014-08-05 02:28:57 -040064 }
65 int ret = BIO_write(bio->next_bio, in, inl);
66 if (ret <= 0) {
67 BIO_copy_next_retry(bio);
68 } else {
David Benjaminaf19de32015-01-11 19:50:31 -050069 a->write_quota -= (a->datagram ? 1 : ret);
David Benjamin43ec06f2014-08-05 02:28:57 -040070 }
71 return ret;
72}
73
David Benjaminc273d2c2015-02-09 12:59:46 -050074static int AsyncRead(BIO *bio, char *out, int outl) {
75 AsyncBio *a = GetData(bio);
David Benjamin43ec06f2014-08-05 02:28:57 -040076 if (a == NULL || bio->next_bio == NULL) {
77 return 0;
78 }
79
80 BIO_clear_retry_flags(bio);
81
82 if (a->read_quota == 0) {
83 BIO_set_retry_read(bio);
84 errno = EAGAIN;
85 return -1;
86 }
87
David Benjamin7e560512023-05-23 11:45:11 -040088 if (!a->datagram && static_cast<size_t>(outl) > a->read_quota) {
89 outl = static_cast<int>(a->read_quota);
David Benjamin43ec06f2014-08-05 02:28:57 -040090 }
91 int ret = BIO_read(bio->next_bio, out, outl);
92 if (ret <= 0) {
93 BIO_copy_next_retry(bio);
94 } else {
David Benjamin6fd297b2014-08-11 18:43:38 -040095 a->read_quota -= (a->datagram ? 1 : ret);
David Benjamin43ec06f2014-08-05 02:28:57 -040096 }
97 return ret;
98}
99
David Benjaminc273d2c2015-02-09 12:59:46 -0500100static long AsyncCtrl(BIO *bio, int cmd, long num, void *ptr) {
David Benjamin43ec06f2014-08-05 02:28:57 -0400101 if (bio->next_bio == NULL) {
102 return 0;
103 }
104 BIO_clear_retry_flags(bio);
David Benjamin02f77052022-11-23 13:00:33 -0500105 long ret = BIO_ctrl(bio->next_bio, cmd, num, ptr);
David Benjamin43ec06f2014-08-05 02:28:57 -0400106 BIO_copy_next_retry(bio);
107 return ret;
108}
109
David Benjaminc273d2c2015-02-09 12:59:46 -0500110static int AsyncNew(BIO *bio) {
111 AsyncBio *a = (AsyncBio *)OPENSSL_malloc(sizeof(*a));
David Benjamin43ec06f2014-08-05 02:28:57 -0400112 if (a == NULL) {
113 return 0;
114 }
David Benjamin17cf2cb2016-12-13 01:07:13 -0500115 OPENSSL_memset(a, 0, sizeof(*a));
David Benjamin13e81fc2015-11-02 17:16:13 -0500116 a->enforce_write_quota = true;
David Benjamin43ec06f2014-08-05 02:28:57 -0400117 bio->init = 1;
118 bio->ptr = (char *)a;
119 return 1;
120}
121
David Benjaminc273d2c2015-02-09 12:59:46 -0500122static int AsyncFree(BIO *bio) {
David Benjamin43ec06f2014-08-05 02:28:57 -0400123 if (bio == NULL) {
124 return 0;
125 }
126
127 OPENSSL_free(bio->ptr);
128 bio->ptr = NULL;
129 bio->init = 0;
130 bio->flags = 0;
131 return 1;
132}
133
David Benjaminc273d2c2015-02-09 12:59:46 -0500134static long AsyncCallbackCtrl(BIO *bio, int cmd, bio_info_cb fp) {
David Benjamin43ec06f2014-08-05 02:28:57 -0400135 if (bio->next_bio == NULL) {
136 return 0;
137 }
138 return BIO_callback_ctrl(bio->next_bio, cmd, fp);
139}
140
David Benjaminc273d2c2015-02-09 12:59:46 -0500141const BIO_METHOD g_async_bio_method = {
David Benjamin43ec06f2014-08-05 02:28:57 -0400142 BIO_TYPE_FILTER,
143 "async bio",
David Benjaminc273d2c2015-02-09 12:59:46 -0500144 AsyncWrite,
145 AsyncRead,
David Benjamin43ec06f2014-08-05 02:28:57 -0400146 NULL /* puts */,
147 NULL /* gets */,
David Benjaminc273d2c2015-02-09 12:59:46 -0500148 AsyncCtrl,
149 AsyncNew,
150 AsyncFree,
151 AsyncCallbackCtrl,
David Benjamin43ec06f2014-08-05 02:28:57 -0400152};
153
154} // namespace
155
Matt Braithwaited17d74d2016-08-17 20:10:28 -0700156bssl::UniquePtr<BIO> AsyncBioCreate() {
157 return bssl::UniquePtr<BIO>(BIO_new(&g_async_bio_method));
David Benjamin43ec06f2014-08-05 02:28:57 -0400158}
159
Matt Braithwaited17d74d2016-08-17 20:10:28 -0700160bssl::UniquePtr<BIO> AsyncBioCreateDatagram() {
161 bssl::UniquePtr<BIO> ret(BIO_new(&g_async_bio_method));
David Benjamin6fd297b2014-08-11 18:43:38 -0400162 if (!ret) {
David Benjamina7f333d2015-02-09 02:37:18 -0500163 return nullptr;
David Benjamin43ec06f2014-08-05 02:28:57 -0400164 }
David Benjaminc273d2c2015-02-09 12:59:46 -0500165 GetData(ret.get())->datagram = true;
David Benjamin6fd297b2014-08-11 18:43:38 -0400166 return ret;
David Benjamin43ec06f2014-08-05 02:28:57 -0400167}
168
David Benjaminc273d2c2015-02-09 12:59:46 -0500169void AsyncBioAllowRead(BIO *bio, size_t count) {
170 AsyncBio *a = GetData(bio);
David Benjamin43ec06f2014-08-05 02:28:57 -0400171 if (a == NULL) {
172 return;
173 }
David Benjamin6fd297b2014-08-11 18:43:38 -0400174 a->read_quota += count;
175}
176
David Benjaminc273d2c2015-02-09 12:59:46 -0500177void AsyncBioAllowWrite(BIO *bio, size_t count) {
178 AsyncBio *a = GetData(bio);
David Benjamin6fd297b2014-08-11 18:43:38 -0400179 if (a == NULL) {
180 return;
181 }
182 a->write_quota += count;
David Benjamin43ec06f2014-08-05 02:28:57 -0400183}
David Benjamin13e81fc2015-11-02 17:16:13 -0500184
185void AsyncBioEnforceWriteQuota(BIO *bio, bool enforce) {
186 AsyncBio *a = GetData(bio);
187 if (a == NULL) {
188 return;
189 }
190 a->enforce_write_quota = enforce;
191}