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/bio.h>
21 #include <openssl/mem.h>
22
23 #include "../../crypto/internal.h"
24
25
26 namespace {
27
28 extern const BIO_METHOD g_async_bio_method;
29
30 struct AsyncBio {
31 bool datagram;
32 bool enforce_write_quota;
33 size_t read_quota;
34 size_t write_quota;
35 };
36
GetData(BIO * bio)37 AsyncBio *GetData(BIO *bio) {
38 if (bio->method != &g_async_bio_method) {
39 return NULL;
40 }
41 return (AsyncBio *)bio->ptr;
42 }
43
AsyncWrite(BIO * bio,const char * in,int inl)44 static int AsyncWrite(BIO *bio, const char *in, int inl) {
45 AsyncBio *a = GetData(bio);
46 if (a == NULL || bio->next_bio == NULL) {
47 return 0;
48 }
49
50 if (!a->enforce_write_quota) {
51 return BIO_write(bio->next_bio, in, inl);
52 }
53
54 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
62 if (!a->datagram && static_cast<size_t>(inl) > a->write_quota) {
63 inl = static_cast<int>(a->write_quota);
64 }
65 int ret = BIO_write(bio->next_bio, in, inl);
66 if (ret <= 0) {
67 BIO_copy_next_retry(bio);
68 } else {
69 a->write_quota -= (a->datagram ? 1 : ret);
70 }
71 return ret;
72 }
73
AsyncRead(BIO * bio,char * out,int outl)74 static int AsyncRead(BIO *bio, char *out, int outl) {
75 AsyncBio *a = GetData(bio);
76 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
88 if (!a->datagram && static_cast<size_t>(outl) > a->read_quota) {
89 outl = static_cast<int>(a->read_quota);
90 }
91 int ret = BIO_read(bio->next_bio, out, outl);
92 if (ret <= 0) {
93 BIO_copy_next_retry(bio);
94 } else {
95 a->read_quota -= (a->datagram ? 1 : ret);
96 }
97 return ret;
98 }
99
AsyncCtrl(BIO * bio,int cmd,long num,void * ptr)100 static long AsyncCtrl(BIO *bio, int cmd, long num, void *ptr) {
101 if (bio->next_bio == NULL) {
102 return 0;
103 }
104 BIO_clear_retry_flags(bio);
105 long ret = BIO_ctrl(bio->next_bio, cmd, num, ptr);
106 BIO_copy_next_retry(bio);
107 return ret;
108 }
109
AsyncNew(BIO * bio)110 static int AsyncNew(BIO *bio) {
111 AsyncBio *a = (AsyncBio *)OPENSSL_zalloc(sizeof(*a));
112 if (a == NULL) {
113 return 0;
114 }
115 a->enforce_write_quota = true;
116 bio->init = 1;
117 bio->ptr = (char *)a;
118 return 1;
119 }
120
AsyncFree(BIO * bio)121 static int AsyncFree(BIO *bio) {
122 if (bio == NULL) {
123 return 0;
124 }
125
126 OPENSSL_free(bio->ptr);
127 bio->ptr = NULL;
128 bio->init = 0;
129 bio->flags = 0;
130 return 1;
131 }
132
AsyncCallbackCtrl(BIO * bio,int cmd,bio_info_cb fp)133 static long AsyncCallbackCtrl(BIO *bio, int cmd, bio_info_cb fp) {
134 if (bio->next_bio == NULL) {
135 return 0;
136 }
137 return BIO_callback_ctrl(bio->next_bio, cmd, fp);
138 }
139
140 const BIO_METHOD g_async_bio_method = {
141 BIO_TYPE_FILTER,
142 "async bio",
143 AsyncWrite,
144 AsyncRead,
145 NULL /* puts */,
146 NULL /* gets */,
147 AsyncCtrl,
148 AsyncNew,
149 AsyncFree,
150 AsyncCallbackCtrl,
151 };
152
153 } // namespace
154
AsyncBioCreate()155 bssl::UniquePtr<BIO> AsyncBioCreate() {
156 return bssl::UniquePtr<BIO>(BIO_new(&g_async_bio_method));
157 }
158
AsyncBioCreateDatagram()159 bssl::UniquePtr<BIO> AsyncBioCreateDatagram() {
160 bssl::UniquePtr<BIO> ret(BIO_new(&g_async_bio_method));
161 if (!ret) {
162 return nullptr;
163 }
164 GetData(ret.get())->datagram = true;
165 return ret;
166 }
167
AsyncBioAllowRead(BIO * bio,size_t count)168 void AsyncBioAllowRead(BIO *bio, size_t count) {
169 AsyncBio *a = GetData(bio);
170 if (a == NULL) {
171 return;
172 }
173 a->read_quota += count;
174 }
175
AsyncBioAllowWrite(BIO * bio,size_t count)176 void AsyncBioAllowWrite(BIO *bio, size_t count) {
177 AsyncBio *a = GetData(bio);
178 if (a == NULL) {
179 return;
180 }
181 a->write_quota += count;
182 }
183
AsyncBioEnforceWriteQuota(BIO * bio,bool enforce)184 void AsyncBioEnforceWriteQuota(BIO *bio, bool enforce) {
185 AsyncBio *a = GetData(bio);
186 if (a == NULL) {
187 return;
188 }
189 a->enforce_write_quota = enforce;
190 }
191