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