1 /* $OpenBSD: evbuffer_tls.c,v 1.1 2015/01/18 19:37:59 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 <stdarg.h> 41 #include <tls.h> 42 43 #include "evbuffer_tls.h" 44 45 /* prototypes */ 46 47 void bufferevent_read_pressure_cb(struct evbuffer *, size_t, size_t, void *); 48 int evtls_read(struct evbuffer *, int, int, struct tls *); 49 int evtls_write(struct evbuffer *, int, struct tls *); 50 51 static int 52 bufferevent_add(struct event *ev, int timeout) 53 { 54 struct timeval tv, *ptv = NULL; 55 56 if (timeout) { 57 timerclear(&tv); 58 tv.tv_sec = timeout; 59 ptv = &tv; 60 } 61 62 return (event_add(ev, ptv)); 63 } 64 65 static void 66 buffertls_readcb(int fd, short event, void *arg) 67 { 68 struct buffertls *buftls = arg; 69 struct bufferevent *bufev = buftls->bt_bufev; 70 struct tls *ctx = buftls->bt_ctx; 71 int res = 0; 72 short what = EVBUFFER_READ; 73 size_t len; 74 int howmuch = -1; 75 76 if (event == EV_TIMEOUT) { 77 what |= EVBUFFER_TIMEOUT; 78 goto error; 79 } 80 81 /* 82 * If we have a high watermark configured then we don't want to 83 * read more data than would make us reach the watermark. 84 */ 85 if (bufev->wm_read.high != 0) { 86 howmuch = bufev->wm_read.high - EVBUFFER_LENGTH(bufev->input); 87 /* we might have lowered the watermark, stop reading */ 88 if (howmuch <= 0) { 89 struct evbuffer *buf = bufev->input; 90 event_del(&bufev->ev_read); 91 evbuffer_setcb(buf, 92 bufferevent_read_pressure_cb, bufev); 93 return; 94 } 95 } 96 97 res = evtls_read(bufev->input, fd, howmuch, ctx); 98 switch (res) { 99 case TLS_READ_AGAIN: 100 event_set(&bufev->ev_read, fd, EV_READ, buffertls_readcb, 101 buftls); 102 goto reschedule; 103 case TLS_WRITE_AGAIN: 104 event_set(&bufev->ev_read, fd, EV_WRITE, buffertls_readcb, 105 buftls); 106 goto reschedule; 107 case -1: 108 if (errno == EAGAIN || errno == EINTR) 109 goto reschedule; 110 /* error case */ 111 what |= EVBUFFER_ERROR; 112 break; 113 case 0: 114 /* eof case */ 115 what |= EVBUFFER_EOF; 116 break; 117 } 118 if (res <= 0) 119 goto error; 120 121 event_set(&bufev->ev_read, fd, EV_READ, buffertls_readcb, buftls); 122 bufferevent_add(&bufev->ev_read, bufev->timeout_read); 123 124 /* See if this callbacks meets the water marks */ 125 len = EVBUFFER_LENGTH(bufev->input); 126 if (bufev->wm_read.low != 0 && len < bufev->wm_read.low) 127 return; 128 if (bufev->wm_read.high != 0 && len >= bufev->wm_read.high) { 129 struct evbuffer *buf = bufev->input; 130 event_del(&bufev->ev_read); 131 132 /* Now schedule a callback for us when the buffer changes */ 133 evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev); 134 } 135 136 /* Invoke the user callback - must always be called last */ 137 if (bufev->readcb != NULL) 138 (*bufev->readcb)(bufev, bufev->cbarg); 139 return; 140 141 reschedule: 142 bufferevent_add(&bufev->ev_read, bufev->timeout_read); 143 return; 144 145 error: 146 (*bufev->errorcb)(bufev, what, bufev->cbarg); 147 } 148 149 static void 150 buffertls_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_READ_AGAIN: 167 event_set(&bufev->ev_write, fd, EV_READ, 168 buffertls_writecb, buftls); 169 goto reschedule; 170 case TLS_WRITE_AGAIN: 171 event_set(&bufev->ev_write, fd, EV_WRITE, 172 buffertls_writecb, buftls); 173 goto reschedule; 174 case -1: 175 if (errno == EAGAIN || errno == EINTR || 176 errno == EINPROGRESS) 177 goto reschedule; 178 /* error case */ 179 what |= EVBUFFER_ERROR; 180 break; 181 case 0: 182 /* eof case */ 183 what |= EVBUFFER_EOF; 184 break; 185 } 186 if (res <= 0) 187 goto error; 188 } 189 190 event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_writecb, buftls); 191 if (EVBUFFER_LENGTH(bufev->output) != 0) 192 bufferevent_add(&bufev->ev_write, bufev->timeout_write); 193 194 /* 195 * Invoke the user callback if our buffer is drained or below the 196 * low watermark. 197 */ 198 if (bufev->writecb != NULL && 199 EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low) 200 (*bufev->writecb)(bufev, bufev->cbarg); 201 202 return; 203 204 reschedule: 205 if (EVBUFFER_LENGTH(bufev->output) != 0) 206 bufferevent_add(&bufev->ev_write, bufev->timeout_write); 207 return; 208 209 error: 210 (*bufev->errorcb)(bufev, what, bufev->cbarg); 211 } 212 213 static void 214 buffertls_connectcb(int fd, short event, void *arg) 215 { 216 struct buffertls *buftls = arg; 217 struct bufferevent *bufev = buftls->bt_bufev; 218 struct tls *ctx = buftls->bt_ctx; 219 const char *hostname = buftls->bt_hostname; 220 int res = 0; 221 short what = EVBUFFER_CONNECT; 222 223 if (event == EV_TIMEOUT) { 224 what |= EVBUFFER_TIMEOUT; 225 goto error; 226 } 227 228 res = tls_connect_socket(ctx, fd, hostname); 229 switch (res) { 230 case TLS_READ_AGAIN: 231 event_set(&bufev->ev_write, fd, EV_READ, 232 buffertls_connectcb, buftls); 233 goto reschedule; 234 case TLS_WRITE_AGAIN: 235 event_set(&bufev->ev_write, fd, EV_WRITE, 236 buffertls_connectcb, buftls); 237 goto reschedule; 238 case -1: 239 if (errno == EAGAIN || errno == EINTR || 240 errno == EINPROGRESS) 241 goto reschedule; 242 /* error case */ 243 what |= EVBUFFER_ERROR; 244 break; 245 } 246 if (res < 0) 247 goto error; 248 249 /* 250 * There might be data available in the tls layer. Try 251 * an read operation and setup the callbacks. Call the read 252 * callback after enabling the write callback to give the 253 * read error handler a chance to disable the write event. 254 */ 255 event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_writecb, buftls); 256 if (EVBUFFER_LENGTH(bufev->output) != 0) 257 bufferevent_add(&bufev->ev_write, bufev->timeout_write); 258 buffertls_readcb(fd, 0, buftls); 259 260 return; 261 262 reschedule: 263 bufferevent_add(&bufev->ev_write, bufev->timeout_write); 264 return; 265 266 error: 267 (*bufev->errorcb)(bufev, what, bufev->cbarg); 268 } 269 270 void 271 buffertls_set(struct buffertls *buftls, struct bufferevent *bufev, 272 struct tls *ctx, int fd) 273 { 274 bufferevent_setfd(bufev, fd); 275 event_set(&bufev->ev_read, fd, EV_READ, buffertls_readcb, buftls); 276 event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_writecb, buftls); 277 buftls->bt_bufev = bufev; 278 buftls->bt_ctx = ctx; 279 } 280 281 void 282 buffertls_connect(struct buffertls *buftls, int fd, const char *hostname) 283 { 284 struct bufferevent *bufev = buftls->bt_bufev; 285 286 event_del(&bufev->ev_read); 287 event_del(&bufev->ev_write); 288 289 buftls->bt_hostname = hostname; 290 buffertls_connectcb(fd, 0, buftls); 291 } 292 293 /* 294 * Reads data from a file descriptor into a buffer. 295 */ 296 297 #define EVBUFFER_MAX_READ 4096 298 299 int 300 evtls_read(struct evbuffer *buf, int fd, int howmuch, struct tls *ctx) 301 { 302 u_char *p; 303 size_t len, oldoff = buf->off; 304 int n = EVBUFFER_MAX_READ; 305 306 if (ioctl(fd, FIONREAD, &n) == -1 || n <= 0) { 307 n = EVBUFFER_MAX_READ; 308 } else if (n > EVBUFFER_MAX_READ && n > howmuch) { 309 /* 310 * It's possible that a lot of data is available for 311 * reading. We do not want to exhaust resources 312 * before the reader has a chance to do something 313 * about it. If the reader does not tell us how much 314 * data we should read, we artifically limit it. 315 */ 316 if ((size_t)n > buf->totallen << 2) 317 n = buf->totallen << 2; 318 if (n < EVBUFFER_MAX_READ) 319 n = EVBUFFER_MAX_READ; 320 } 321 if (howmuch < 0 || howmuch > n) 322 howmuch = n; 323 324 /* If we don't have FIONREAD, we might waste some space here */ 325 if (evbuffer_expand(buf, howmuch) == -1) 326 return (-1); 327 328 /* We can append new data at this point */ 329 p = buf->buffer + buf->off; 330 331 n = tls_read(ctx, p, howmuch, &len); 332 if (n < 0 || len == 0) 333 return (n); 334 335 buf->off += len; 336 337 /* Tell someone about changes in this buffer */ 338 if (buf->off != oldoff && buf->cb != NULL) 339 (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); 340 341 return (len); 342 } 343 344 int 345 evtls_write(struct evbuffer *buffer, int fd, struct tls *ctx) 346 { 347 size_t len; 348 int n; 349 350 n = tls_write(ctx, buffer->buffer, buffer->off, &len); 351 if (n < 0 || len == 0) 352 return (n); 353 354 evbuffer_drain(buffer, len); 355 356 return (len); 357 } 358