blob: c007ffa9f938c0b54208647e2b74fb1e79296132 [file] [log] [blame]
David Benjamin43ec06f2014-08-05 02:28:57 -04001/* 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>
Adam Langley2b2d66d2015-01-30 17:08:37 -080018#include <string.h>
19
David Benjamin43ec06f2014-08-05 02:28:57 -040020#include <openssl/mem.h>
21
Adam Langley2b2d66d2015-01-30 17:08:37 -080022
David Benjamin43ec06f2014-08-05 02:28:57 -040023namespace {
24
25extern const BIO_METHOD async_bio_method;
26
27struct async_bio {
David Benjamin6fd297b2014-08-11 18:43:38 -040028 bool datagram;
David Benjamin43ec06f2014-08-05 02:28:57 -040029 size_t read_quota;
30 size_t write_quota;
31};
32
33async_bio *get_data(BIO *bio) {
34 if (bio->method != &async_bio_method) {
35 return NULL;
36 }
37 return (async_bio *)bio->ptr;
38}
39
40static int async_write(BIO *bio, const char *in, int inl) {
41 async_bio *a = get_data(bio);
42 if (a == NULL || bio->next_bio == NULL) {
43 return 0;
44 }
45
David Benjamin6fd297b2014-08-11 18:43:38 -040046 if (a->datagram) {
47 // Perform writes synchronously; the DTLS implementation drops any packets
48 // that failed to send.
49 return BIO_write(bio->next_bio, in, inl);
50 }
51
David Benjamin43ec06f2014-08-05 02:28:57 -040052 BIO_clear_retry_flags(bio);
53
54 if (a->write_quota == 0) {
55 BIO_set_retry_write(bio);
56 errno = EAGAIN;
57 return -1;
58 }
59
David Benjamin6fd297b2014-08-11 18:43:38 -040060 if (!a->datagram && (size_t)inl > a->write_quota) {
David Benjamin43ec06f2014-08-05 02:28:57 -040061 inl = a->write_quota;
62 }
63 int ret = BIO_write(bio->next_bio, in, inl);
64 if (ret <= 0) {
65 BIO_copy_next_retry(bio);
66 } else {
David Benjaminaf19de32015-01-11 19:50:31 -050067 a->write_quota -= (a->datagram ? 1 : ret);
David Benjamin43ec06f2014-08-05 02:28:57 -040068 }
69 return ret;
70}
71
72static int async_read(BIO *bio, char *out, int outl) {
73 async_bio *a = get_data(bio);
74 if (a == NULL || bio->next_bio == NULL) {
75 return 0;
76 }
77
78 BIO_clear_retry_flags(bio);
79
80 if (a->read_quota == 0) {
81 BIO_set_retry_read(bio);
82 errno = EAGAIN;
83 return -1;
84 }
85
David Benjamin6fd297b2014-08-11 18:43:38 -040086 if (!a->datagram && (size_t)outl > a->read_quota) {
David Benjamin43ec06f2014-08-05 02:28:57 -040087 outl = a->read_quota;
88 }
89 int ret = BIO_read(bio->next_bio, out, outl);
90 if (ret <= 0) {
91 BIO_copy_next_retry(bio);
92 } else {
David Benjamin6fd297b2014-08-11 18:43:38 -040093 a->read_quota -= (a->datagram ? 1 : ret);
David Benjamin43ec06f2014-08-05 02:28:57 -040094 }
95 return ret;
96}
97
98static long async_ctrl(BIO *bio, int cmd, long num, void *ptr) {
99 if (bio->next_bio == NULL) {
100 return 0;
101 }
102 BIO_clear_retry_flags(bio);
103 int ret = BIO_ctrl(bio->next_bio, cmd, num, ptr);
104 BIO_copy_next_retry(bio);
105 return ret;
106}
107
108static int async_new(BIO *bio) {
109 async_bio *a = (async_bio *)OPENSSL_malloc(sizeof(*a));
110 if (a == NULL) {
111 return 0;
112 }
113 memset(a, 0, sizeof(*a));
114 bio->init = 1;
115 bio->ptr = (char *)a;
116 return 1;
117}
118
119static int async_free(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
131static long async_callback_ctrl(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
138const BIO_METHOD async_bio_method = {
139 BIO_TYPE_FILTER,
140 "async bio",
141 async_write,
142 async_read,
143 NULL /* puts */,
144 NULL /* gets */,
145 async_ctrl,
146 async_new,
147 async_free,
148 async_callback_ctrl,
149};
150
151} // namespace
152
153BIO *async_bio_create() {
154 return BIO_new(&async_bio_method);
155}
156
David Benjamin6fd297b2014-08-11 18:43:38 -0400157BIO *async_bio_create_datagram() {
158 BIO *ret = BIO_new(&async_bio_method);
159 if (!ret) {
160 return NULL;
David Benjamin43ec06f2014-08-05 02:28:57 -0400161 }
David Benjamin6fd297b2014-08-11 18:43:38 -0400162 get_data(ret)->datagram = true;
163 return ret;
David Benjamin43ec06f2014-08-05 02:28:57 -0400164}
165
David Benjamin6fd297b2014-08-11 18:43:38 -0400166void async_bio_allow_read(BIO *bio, size_t count) {
David Benjamin43ec06f2014-08-05 02:28:57 -0400167 async_bio *a = get_data(bio);
168 if (a == NULL) {
169 return;
170 }
David Benjamin6fd297b2014-08-11 18:43:38 -0400171 a->read_quota += count;
172}
173
174void async_bio_allow_write(BIO *bio, size_t count) {
175 async_bio *a = get_data(bio);
176 if (a == NULL) {
177 return;
178 }
179 a->write_quota += count;
David Benjamin43ec06f2014-08-05 02:28:57 -0400180}