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 34 AsyncBio *GetData(BIO *bio) { 35 if (bio->method != &g_async_bio_method) { 36 return NULL; 37 } 38 return (AsyncBio *)bio->ptr; 39 } 40 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 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 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 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 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 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 153 ScopedBIO AsyncBioCreate() { 154 return ScopedBIO(BIO_new(&g_async_bio_method)); 155 } 156 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 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 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 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