1 /* 2 * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the OpenSSL license (the "License"). You may not use 5 * this file except in compliance with the License. You can obtain a copy 6 * in the file LICENSE in the source distribution or at 7 * https://www.openssl.org/source/license.html 8 */ 9 10 #include "async_bio.h" 11 12 #include <errno.h> 13 #include <string.h> 14 15 #include <openssl/bio.h> 16 #include <openssl/crypto.h> 17 18 19 namespace { 20 21 struct AsyncBio { 22 bool datagram; 23 bool enforce_write_quota; 24 size_t read_quota; 25 size_t write_quota; 26 }; 27 28 AsyncBio *GetData(BIO *bio) { 29 return (AsyncBio *)BIO_get_data(bio); 30 } 31 32 static int AsyncWrite(BIO *bio, const char *in, int inl) { 33 AsyncBio *a = GetData(bio); 34 if (a == NULL || BIO_next(bio) == NULL) { 35 return 0; 36 } 37 38 if (!a->enforce_write_quota) { 39 return BIO_write(BIO_next(bio), in, inl); 40 } 41 42 BIO_clear_retry_flags(bio); 43 44 if (a->write_quota == 0) { 45 BIO_set_retry_write(bio); 46 errno = EAGAIN; 47 return -1; 48 } 49 50 if (!a->datagram && (size_t)inl > a->write_quota) { 51 inl = a->write_quota; 52 } 53 int ret = BIO_write(BIO_next(bio), in, inl); 54 if (ret <= 0) { 55 BIO_copy_next_retry(bio); 56 } else { 57 a->write_quota -= (a->datagram ? 1 : ret); 58 } 59 return ret; 60 } 61 62 static int AsyncRead(BIO *bio, char *out, int outl) { 63 AsyncBio *a = GetData(bio); 64 if (a == NULL || BIO_next(bio) == NULL) { 65 return 0; 66 } 67 68 BIO_clear_retry_flags(bio); 69 70 if (a->read_quota == 0) { 71 BIO_set_retry_read(bio); 72 errno = EAGAIN; 73 return -1; 74 } 75 76 if (!a->datagram && (size_t)outl > a->read_quota) { 77 outl = a->read_quota; 78 } 79 int ret = BIO_read(BIO_next(bio), out, outl); 80 if (ret <= 0) { 81 BIO_copy_next_retry(bio); 82 } else { 83 a->read_quota -= (a->datagram ? 1 : ret); 84 } 85 return ret; 86 } 87 88 static long AsyncCtrl(BIO *bio, int cmd, long num, void *ptr) { 89 if (BIO_next(bio) == NULL) { 90 return 0; 91 } 92 BIO_clear_retry_flags(bio); 93 int ret = BIO_ctrl(BIO_next(bio), cmd, num, ptr); 94 BIO_copy_next_retry(bio); 95 return ret; 96 } 97 98 static int AsyncNew(BIO *bio) { 99 AsyncBio *a = (AsyncBio *)OPENSSL_malloc(sizeof(*a)); 100 if (a == NULL) { 101 return 0; 102 } 103 memset(a, 0, sizeof(*a)); 104 a->enforce_write_quota = true; 105 BIO_set_init(bio, 1); 106 BIO_set_data(bio, a); 107 return 1; 108 } 109 110 static int AsyncFree(BIO *bio) { 111 if (bio == NULL) { 112 return 0; 113 } 114 115 OPENSSL_free(BIO_get_data(bio)); 116 BIO_set_data(bio, NULL); 117 BIO_set_init(bio, 0); 118 return 1; 119 } 120 121 static long AsyncCallbackCtrl(BIO *bio, int cmd, BIO_info_cb fp) 122 { 123 if (BIO_next(bio) == NULL) 124 return 0; 125 return BIO_callback_ctrl(BIO_next(bio), cmd, fp); 126 } 127 128 static BIO_METHOD *g_async_bio_method = NULL; 129 130 static const BIO_METHOD *AsyncMethod(void) 131 { 132 if (g_async_bio_method == NULL) { 133 g_async_bio_method = BIO_meth_new(BIO_TYPE_FILTER, "async bio"); 134 if ( g_async_bio_method == NULL 135 || !BIO_meth_set_write(g_async_bio_method, AsyncWrite) 136 || !BIO_meth_set_read(g_async_bio_method, AsyncRead) 137 || !BIO_meth_set_ctrl(g_async_bio_method, AsyncCtrl) 138 || !BIO_meth_set_create(g_async_bio_method, AsyncNew) 139 || !BIO_meth_set_destroy(g_async_bio_method, AsyncFree) 140 || !BIO_meth_set_callback_ctrl(g_async_bio_method, AsyncCallbackCtrl)) 141 return NULL; 142 } 143 return g_async_bio_method; 144 } 145 146 } // namespace 147 148 bssl::UniquePtr<BIO> AsyncBioCreate() { 149 return bssl::UniquePtr<BIO>(BIO_new(AsyncMethod())); 150 } 151 152 bssl::UniquePtr<BIO> AsyncBioCreateDatagram() { 153 bssl::UniquePtr<BIO> ret(BIO_new(AsyncMethod())); 154 if (!ret) { 155 return nullptr; 156 } 157 GetData(ret.get())->datagram = true; 158 return ret; 159 } 160 161 void AsyncBioAllowRead(BIO *bio, size_t count) { 162 AsyncBio *a = GetData(bio); 163 if (a == NULL) { 164 return; 165 } 166 a->read_quota += count; 167 } 168 169 void AsyncBioAllowWrite(BIO *bio, size_t count) { 170 AsyncBio *a = GetData(bio); 171 if (a == NULL) { 172 return; 173 } 174 a->write_quota += count; 175 } 176 177 void AsyncBioEnforceWriteQuota(BIO *bio, bool enforce) { 178 AsyncBio *a = GetData(bio); 179 if (a == NULL) { 180 return; 181 } 182 a->enforce_write_quota = enforce; 183 } 184