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