jcs's openbsd hax
openbsd
1/* $OpenBSD: evbuffer_tls.c,v 1.14 2024/11/07 10:12:18 bluhm Exp $ */
2
3/*
4 * Copyright (c) 2002-2004 Niels Provos <provos@citi.umich.edu>
5 * Copyright (c) 2014-2015 Alexander Bluhm <bluhm@openbsd.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <sys/types.h>
32#include <sys/time.h>
33#include <sys/ioctl.h>
34
35#include <errno.h>
36#include <event.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <tls.h>
41
42#include "evbuffer_tls.h"
43
44/* prototypes */
45
46void bufferevent_read_pressure_cb(struct evbuffer *, size_t, size_t, void *);
47static void buffertls_readcb(int, short, void *);
48static void buffertls_writecb(int, short, void *);
49static void buffertls_handshakecb(int, short, void *);
50int evtls_read(struct evbuffer *, int, int, struct tls *);
51int evtls_write(struct evbuffer *, int, struct tls *);
52
53static int
54bufferevent_add(struct event *ev, int timeout)
55{
56 struct timeval tv, *ptv = NULL;
57
58 if (timeout) {
59 timerclear(&tv);
60 tv.tv_sec = timeout;
61 ptv = &tv;
62 }
63
64 return (event_add(ev, ptv));
65}
66
67static void
68buffertls_readcb(int fd, short event, void *arg)
69{
70 struct buffertls *buftls = arg;
71 struct bufferevent *bufev = buftls->bt_bufev;
72 struct tls *ctx = buftls->bt_ctx;
73 int res = 0;
74 short what = EVBUFFER_READ;
75 size_t len;
76 int howmuch = -1;
77
78 if (event == EV_TIMEOUT) {
79 what |= EVBUFFER_TIMEOUT;
80 goto error;
81 }
82
83 /*
84 * If we have a high watermark configured then we don't want to
85 * read more data than would make us reach the watermark.
86 */
87 if (bufev->wm_read.high != 0) {
88 howmuch = bufev->wm_read.high - EVBUFFER_LENGTH(bufev->input);
89 /* we might have lowered the watermark, stop reading */
90 if (howmuch <= 0) {
91 struct evbuffer *buf = bufev->input;
92 event_del(&bufev->ev_read);
93 evbuffer_setcb(buf,
94 bufferevent_read_pressure_cb, bufev);
95 return;
96 }
97 }
98
99 res = evtls_read(bufev->input, fd, howmuch, ctx);
100 switch (res) {
101 case TLS_WANT_POLLIN:
102 bufferevent_add(&bufev->ev_read, bufev->timeout_read);
103 return;
104 case TLS_WANT_POLLOUT:
105 event_del(&bufev->ev_write);
106 event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_readcb,
107 buftls);
108 bufferevent_add(&bufev->ev_write, bufev->timeout_write);
109 return;
110 case -1:
111 what |= EVBUFFER_ERROR;
112 break;
113 case 0:
114 tls_close(ctx);
115 what |= EVBUFFER_EOF;
116 break;
117 }
118 if (res <= 0)
119 goto error;
120
121 event_del(&bufev->ev_write);
122 event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_writecb, buftls);
123 if (bufev->enabled & EV_READ)
124 bufferevent_add(&bufev->ev_read, bufev->timeout_read);
125 if (EVBUFFER_LENGTH(bufev->output) != 0 && bufev->enabled & EV_WRITE)
126 bufferevent_add(&bufev->ev_write, bufev->timeout_write);
127
128 /* See if this callbacks meets the water marks */
129 len = EVBUFFER_LENGTH(bufev->input);
130 if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
131 return;
132 if (bufev->wm_read.high != 0 && len >= bufev->wm_read.high) {
133 struct evbuffer *buf = bufev->input;
134 event_del(&bufev->ev_read);
135
136 /* Now schedule a callback for us when the buffer changes */
137 evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev);
138 }
139
140 /* Invoke the user callback - must always be called last */
141 if (bufev->readcb != NULL)
142 (*bufev->readcb)(bufev, bufev->cbarg);
143 return;
144
145 error:
146 (*bufev->errorcb)(bufev, what, bufev->cbarg);
147}
148
149static void
150buffertls_writecb(int fd, short event, void *arg)
151{
152 struct buffertls *buftls = arg;
153 struct bufferevent *bufev = buftls->bt_bufev;
154 struct tls *ctx = buftls->bt_ctx;
155 int res = 0;
156 short what = EVBUFFER_WRITE;
157
158 if (event == EV_TIMEOUT) {
159 what |= EVBUFFER_TIMEOUT;
160 goto error;
161 }
162
163 if (EVBUFFER_LENGTH(bufev->output) != 0) {
164 res = evtls_write(bufev->output, fd, ctx);
165 switch (res) {
166 case TLS_WANT_POLLIN:
167 event_del(&bufev->ev_read);
168 event_set(&bufev->ev_read, fd, EV_READ,
169 buffertls_writecb, buftls);
170 bufferevent_add(&bufev->ev_read, bufev->timeout_read);
171 return;
172 case TLS_WANT_POLLOUT:
173 bufferevent_add(&bufev->ev_write, bufev->timeout_write);
174 return;
175 case -1:
176 what |= EVBUFFER_ERROR;
177 break;
178 case 0:
179 what |= EVBUFFER_EOF;
180 break;
181 }
182 if (res <= 0)
183 goto error;
184 }
185
186 event_del(&bufev->ev_read);
187 event_set(&bufev->ev_read, fd, EV_READ, buffertls_readcb, buftls);
188 if (bufev->enabled & EV_READ)
189 bufferevent_add(&bufev->ev_read, bufev->timeout_read);
190 if (EVBUFFER_LENGTH(bufev->output) != 0 && bufev->enabled & EV_WRITE)
191 bufferevent_add(&bufev->ev_write, bufev->timeout_write);
192
193 /*
194 * Invoke the user callback if our buffer is drained or below the
195 * low watermark.
196 */
197 if (bufev->writecb != NULL &&
198 EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
199 (*bufev->writecb)(bufev, bufev->cbarg);
200
201 return;
202
203 error:
204 (*bufev->errorcb)(bufev, what, bufev->cbarg);
205}
206
207static void
208buffertls_handshakecb(int fd, short event, void *arg)
209{
210 struct buffertls *buftls = arg;
211 struct bufferevent *bufev = buftls->bt_bufev;
212 struct tls *ctx = buftls->bt_ctx;
213 int res = 0;
214 short what = EVBUFFER_HANDSHAKE;
215
216 if (event == EV_TIMEOUT) {
217 what |= EVBUFFER_TIMEOUT;
218 goto error;
219 }
220
221 res = tls_handshake(ctx);
222 switch (res) {
223 case TLS_WANT_POLLIN:
224 bufferevent_add(&bufev->ev_read, bufev->timeout_read);
225 return;
226 case TLS_WANT_POLLOUT:
227 bufferevent_add(&bufev->ev_write, bufev->timeout_write);
228 return;
229 case -1:
230 what |= EVBUFFER_ERROR;
231 break;
232 }
233 if (res == -1)
234 goto error;
235
236 /* Handshake was successful, change to read and write callback. */
237 event_del(&bufev->ev_read);
238 event_del(&bufev->ev_write);
239 event_set(&bufev->ev_read, fd, EV_READ, buffertls_readcb, buftls);
240 event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_writecb, buftls);
241 if (bufev->enabled & EV_READ)
242 bufferevent_add(&bufev->ev_read, bufev->timeout_read);
243 if (bufev->enabled & EV_WRITE)
244 bufferevent_add(&bufev->ev_write, bufev->timeout_write);
245
246 return;
247
248 error:
249 (*bufev->errorcb)(bufev, what, bufev->cbarg);
250}
251
252void
253buffertls_set(struct buffertls *buftls, struct bufferevent *bufev,
254 struct tls *ctx, int fd)
255{
256 bufferevent_setfd(bufev, fd);
257 event_set(&bufev->ev_read, fd, EV_READ, buffertls_readcb, buftls);
258 event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_writecb, buftls);
259 buftls->bt_bufev = bufev;
260 buftls->bt_ctx = ctx;
261}
262
263void
264buffertls_accept(struct buffertls *buftls, int fd)
265{
266 struct bufferevent *bufev = buftls->bt_bufev;
267
268 event_del(&bufev->ev_read);
269 event_del(&bufev->ev_write);
270 event_set(&bufev->ev_read, fd, EV_READ, buffertls_handshakecb, buftls);
271 event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_handshakecb,
272 buftls);
273 bufferevent_add(&bufev->ev_read, bufev->timeout_read);
274}
275
276void
277buffertls_connect(struct buffertls *buftls, int fd)
278{
279 struct bufferevent *bufev = buftls->bt_bufev;
280
281 event_del(&bufev->ev_read);
282 event_del(&bufev->ev_write);
283 event_set(&bufev->ev_read, fd, EV_READ, buffertls_handshakecb, buftls);
284 event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_handshakecb,
285 buftls);
286 bufferevent_add(&bufev->ev_write, bufev->timeout_write);
287}
288
289/*
290 * Reads data from a file descriptor into a buffer.
291 */
292
293#define EVBUFFER_MAX_READ 16384
294
295int
296evtls_read(struct evbuffer *buf, int fd, int howmuch, struct tls *ctx)
297{
298 u_char *p;
299 size_t oldoff = buf->off;
300 int n = EVBUFFER_MAX_READ;
301
302 if (howmuch < 0 || howmuch > n)
303 howmuch = n;
304
305 /* If we don't have FIONREAD, we might waste some space here */
306 if (evbuffer_expand(buf, howmuch) == -1)
307 return (-1);
308
309 /* We can append new data at this point */
310 p = buf->buffer + buf->off;
311
312 n = tls_read(ctx, p, howmuch);
313 if (n <= 0)
314 return (n);
315
316 buf->off += n;
317
318 /* Tell someone about changes in this buffer */
319 if (buf->off != oldoff && buf->cb != NULL)
320 (*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
321
322 return (n);
323}
324
325int
326evtls_write(struct evbuffer *buffer, int fd, struct tls *ctx)
327{
328 int n;
329
330 n = tls_write(ctx, buffer->buffer, buffer->off);
331 if (n <= 0)
332 return (n);
333 evbuffer_drain(buffer, n);
334
335 return (n);
336}