Integrate BoringSSL shim
[openssl.git] / test / ossl_shim / async_bio.cc
1 /* 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>
18 #include <string.h>
19
20 #include <openssl/crypto.h>
21
22
23 namespace {
24
25 struct AsyncBio {
26   bool datagram;
27   bool enforce_write_quota;
28   size_t read_quota;
29   size_t write_quota;
30 };
31
32 AsyncBio *GetData(BIO *bio) {
33   /*
34    * TODO: Missing accessor? This probably needs a BIO_get_method() in OpenSSL
35    *       For now skip this check
36    */
37 #if 0
38   if (bio->method != &g_async_bio_method) {
39     return NULL;
40   }
41 #endif
42   return (AsyncBio *)BIO_get_data(bio);
43 }
44
45 static int AsyncWrite(BIO *bio, const char *in, int inl) {
46   AsyncBio *a = GetData(bio);
47   if (a == NULL || BIO_next(bio) == NULL) {
48     return 0;
49   }
50
51   if (!a->enforce_write_quota) {
52     return BIO_write(BIO_next(bio), in, inl);
53   }
54
55   BIO_clear_retry_flags(bio);
56
57   if (a->write_quota == 0) {
58     BIO_set_retry_write(bio);
59     errno = EAGAIN;
60     return -1;
61   }
62
63   if (!a->datagram && (size_t)inl > a->write_quota) {
64     inl = a->write_quota;
65   }
66   int ret = BIO_write(BIO_next(bio), in, inl);
67   if (ret <= 0) {
68     BIO_copy_next_retry(bio);
69   } else {
70     a->write_quota -= (a->datagram ? 1 : ret);
71   }
72   return ret;
73 }
74
75 static int AsyncRead(BIO *bio, char *out, int outl) {
76   AsyncBio *a = GetData(bio);
77   if (a == NULL || BIO_next(bio) == NULL) {
78     return 0;
79   }
80
81   BIO_clear_retry_flags(bio);
82
83   if (a->read_quota == 0) {
84     BIO_set_retry_read(bio);
85     errno = EAGAIN;
86     return -1;
87   }
88
89   if (!a->datagram && (size_t)outl > a->read_quota) {
90     outl = a->read_quota;
91   }
92   int ret = BIO_read(BIO_next(bio), out, outl);
93   if (ret <= 0) {
94     BIO_copy_next_retry(bio);
95   } else {
96     a->read_quota -= (a->datagram ? 1 : ret);
97   }
98   return ret;
99 }
100
101 static long AsyncCtrl(BIO *bio, int cmd, long num, void *ptr) {
102   if (BIO_next(bio) == NULL) {
103     return 0;
104   }
105   BIO_clear_retry_flags(bio);
106   int ret = BIO_ctrl(BIO_next(bio), cmd, num, ptr);
107   BIO_copy_next_retry(bio);
108   return ret;
109 }
110
111 static int AsyncNew(BIO *bio) {
112   AsyncBio *a = (AsyncBio *)OPENSSL_malloc(sizeof(*a));
113   if (a == NULL) {
114     return 0;
115   }
116   memset(a, 0, sizeof(*a));
117   a->enforce_write_quota = true;
118   BIO_set_init(bio, 1);
119   BIO_set_data(bio, a);
120   return 1;
121 }
122
123 static int AsyncFree(BIO *bio) {
124   if (bio == NULL) {
125     return 0;
126   }
127
128   OPENSSL_free(BIO_get_data(bio));
129   BIO_set_data(bio, NULL);
130   BIO_set_init(bio, 0);
131   return 1;
132 }
133
134 static long AsyncCallbackCtrl(BIO *bio, int cmd, bio_info_cb fp) {
135   if (BIO_next(bio) == NULL) {
136     return 0;
137   }
138   return BIO_callback_ctrl(BIO_next(bio), cmd, fp);
139 }
140
141 static BIO_METHOD *g_async_bio_method = NULL;
142
143 static const BIO_METHOD *AsyncMethod(void)
144 {
145   if (g_async_bio_method == NULL) {
146     g_async_bio_method = BIO_meth_new(BIO_TYPE_FILTER, "async bio");
147     if (   g_async_bio_method == NULL
148         || !BIO_meth_set_write(g_async_bio_method, AsyncWrite)
149         || !BIO_meth_set_read(g_async_bio_method, AsyncRead)
150         || !BIO_meth_set_ctrl(g_async_bio_method, AsyncCtrl)
151         || !BIO_meth_set_create(g_async_bio_method, AsyncNew)
152         || !BIO_meth_set_destroy(g_async_bio_method, AsyncFree)
153         || !BIO_meth_set_callback_ctrl(g_async_bio_method, AsyncCallbackCtrl))
154     return NULL;
155   }
156   return g_async_bio_method;
157 }
158
159 }  // namespace
160
161 ScopedBIO AsyncBioCreate() {
162   return ScopedBIO(BIO_new(AsyncMethod()));
163 }
164
165 ScopedBIO AsyncBioCreateDatagram() {
166   ScopedBIO ret(BIO_new(AsyncMethod()));
167   if (!ret) {
168     return nullptr;
169   }
170   GetData(ret.get())->datagram = true;
171   return ret;
172 }
173
174 void AsyncBioAllowRead(BIO *bio, size_t count) {
175   AsyncBio *a = GetData(bio);
176   if (a == NULL) {
177     return;
178   }
179   a->read_quota += count;
180 }
181
182 void AsyncBioAllowWrite(BIO *bio, size_t count) {
183   AsyncBio *a = GetData(bio);
184   if (a == NULL) {
185     return;
186   }
187   a->write_quota += count;
188 }
189
190 void AsyncBioEnforceWriteQuota(BIO *bio, bool enforce) {
191   AsyncBio *a = GetData(bio);
192   if (a == NULL) {
193     return;
194   }
195   a->enforce_write_quota = enforce;
196 }