1 // Copyright 2014 The BoringSSL Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
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 struct AsyncBio {
29   bool datagram = false;
30   size_t read_quota = 0;
31   size_t write_quota = 0;
32 };
33 
AsyncBioMethodType()34 static int AsyncBioMethodType() {
35   static int type = [] {
36     int idx = BIO_get_new_index();
37     BSSL_CHECK(idx > 0);
38     return idx | BIO_TYPE_FILTER;
39   }();
40   return type;
41 }
42 
GetData(BIO * bio)43 AsyncBio *GetData(BIO *bio) {
44   if (BIO_method_type(bio) != AsyncBioMethodType()) {
45     return nullptr;
46   }
47   return static_cast<AsyncBio *>(BIO_get_data(bio));
48 }
49 
AsyncWrite(BIO * bio,const char * in,int inl)50 static int AsyncWrite(BIO *bio, const char *in, int inl) {
51   AsyncBio *a = GetData(bio);
52   BIO *next = BIO_next(bio);
53   if (a == nullptr || next == nullptr) {
54     return 0;
55   }
56 
57   BIO_clear_retry_flags(bio);
58 
59   if (a->write_quota == 0) {
60     BIO_set_retry_write(bio);
61     errno = EAGAIN;
62     return -1;
63   }
64 
65   if (!a->datagram && static_cast<size_t>(inl) > a->write_quota) {
66     inl = static_cast<int>(a->write_quota);
67   }
68   int ret = BIO_write(next, in, inl);
69   if (ret <= 0) {
70     BIO_copy_next_retry(bio);
71   } else {
72     a->write_quota -= (a->datagram ? 1 : ret);
73   }
74   return ret;
75 }
76 
AsyncRead(BIO * bio,char * out,int outl)77 static int AsyncRead(BIO *bio, char *out, int outl) {
78   AsyncBio *a = GetData(bio);
79   BIO *next = BIO_next(bio);
80   if (a == nullptr || next == nullptr) {
81     return 0;
82   }
83 
84   BIO_clear_retry_flags(bio);
85 
86   if (a->read_quota == 0) {
87     BIO_set_retry_read(bio);
88     errno = EAGAIN;
89     return -1;
90   }
91 
92   if (!a->datagram && static_cast<size_t>(outl) > a->read_quota) {
93     outl = static_cast<int>(a->read_quota);
94   }
95   int ret = BIO_read(next, out, outl);
96   if (ret <= 0) {
97     BIO_copy_next_retry(bio);
98   } else {
99     a->read_quota -= (a->datagram ? 1 : ret);
100   }
101   return ret;
102 }
103 
AsyncCtrl(BIO * bio,int cmd,long num,void * ptr)104 static long AsyncCtrl(BIO *bio, int cmd, long num, void *ptr) {
105   AsyncBio *a = GetData(bio);
106   BIO *next = BIO_next(bio);
107   if (next == nullptr) {
108     return 0;
109   }
110   BIO_clear_retry_flags(bio);
111   // Model an async flush as consuming one byte of write quota.
112   if (cmd == BIO_CTRL_FLUSH && a->write_quota == 0) {
113     BIO_set_retry_write(bio);
114     errno = EAGAIN;
115     return -1;
116   }
117   long ret = BIO_ctrl(next, cmd, num, ptr);
118   if (cmd == BIO_CTRL_FLUSH && ret > 0) {
119     a->write_quota--;
120   }
121   BIO_copy_next_retry(bio);
122   return ret;
123 }
124 
AsyncNew(BIO * bio)125 static int AsyncNew(BIO *bio) {
126   BIO_set_data(bio, new AsyncBio);
127   BIO_set_init(bio, 1);
128   return 1;
129 }
130 
AsyncFree(BIO * bio)131 static int AsyncFree(BIO *bio) {
132   if (bio == nullptr) {
133     return 0;
134   }
135 
136   delete GetData(bio);
137   BIO_set_data(bio, nullptr);
138   BIO_set_init(bio, 0);
139   return 1;
140 }
141 
AsyncCallbackCtrl(BIO * bio,int cmd,BIO_info_cb * fp)142 static long AsyncCallbackCtrl(BIO *bio, int cmd, BIO_info_cb *fp) {
143   BIO *next = BIO_next(bio);
144   if (next == nullptr) {
145     return 0;
146   }
147   return BIO_callback_ctrl(next, cmd, fp);
148 }
149 
AsyncBioMethod()150 static const BIO_METHOD *AsyncBioMethod() {
151   static const BIO_METHOD *method = [] {
152     BIO_METHOD *ret = BIO_meth_new(AsyncBioMethodType(), "async bio");
153     BSSL_CHECK(ret);
154     BSSL_CHECK(BIO_meth_set_write(ret, AsyncWrite));
155     BSSL_CHECK(BIO_meth_set_read(ret, AsyncRead));
156     BSSL_CHECK(BIO_meth_set_ctrl(ret, AsyncCtrl));
157     BSSL_CHECK(BIO_meth_set_create(ret, AsyncNew));
158     BSSL_CHECK(BIO_meth_set_destroy(ret, AsyncFree));
159     BSSL_CHECK(BIO_meth_set_callback_ctrl(ret, AsyncCallbackCtrl));
160     return ret;
161   }();
162   return method;
163 }
164 
165 }  // namespace
166 
AsyncBioCreate()167 bssl::UniquePtr<BIO> AsyncBioCreate() {
168   return bssl::UniquePtr<BIO>(BIO_new(AsyncBioMethod()));
169 }
170 
AsyncBioCreateDatagram()171 bssl::UniquePtr<BIO> AsyncBioCreateDatagram() {
172   bssl::UniquePtr<BIO> ret(BIO_new(AsyncBioMethod()));
173   if (!ret) {
174     return nullptr;
175   }
176   GetData(ret.get())->datagram = true;
177   return ret;
178 }
179 
AsyncBioAllowRead(BIO * bio,size_t count)180 void AsyncBioAllowRead(BIO *bio, size_t count) {
181   AsyncBio *a = GetData(bio);
182   if (a == nullptr) {
183     return;
184   }
185   a->read_quota += count;
186 }
187 
AsyncBioAllowWrite(BIO * bio,size_t count)188 void AsyncBioAllowWrite(BIO *bio, size_t count) {
189   AsyncBio *a = GetData(bio);
190   if (a == nullptr) {
191     return;
192   }
193   a->write_quota += count;
194 }
195