X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=netatalk.git;a=blobdiff_plain;f=libevent%2Fbufferevent_openssl.c;h=412c08e5c0b68007066c65b7416cadab5a5bbd1c;hp=3d9a77012f0e396314500a645d9b3f89aaefa610;hb=3a84db87064922ad10ac10cc1d6833380e575995;hpb=5ab4f0463c00263b3d842f1e1a3648ecf35b4d65 diff --git a/libevent/bufferevent_openssl.c b/libevent/bufferevent_openssl.c index 3d9a7701..412c08e5 100644 --- a/libevent/bufferevent_openssl.c +++ b/libevent/bufferevent_openssl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2010 Niels Provos and Nick Mathewson + * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -170,8 +170,8 @@ bio_bufferevent_write(BIO *b, const char *in, int inlen) /* Copy only as much data onto the output buffer as can fit under the * high-water mark. */ - if (bufev->wm_write.high && bufev->wm_write.high >= (outlen+inlen)) { - if (bufev->wm_write.high >= outlen) { + if (bufev->wm_write.high && bufev->wm_write.high <= (outlen+inlen)) { + if (bufev->wm_write.high <= outlen) { /* If no data can fit, we'll need to retry later. */ BIO_set_retry_write(b); return -1; @@ -704,6 +704,50 @@ do_write(struct bufferevent_openssl *bev_ssl, int atmost) #define READ_DEFAULT 4096 +/* Try to figure out how many bytes to read; return 0 if we shouldn't be + * reading. */ +static int +bytes_to_read(struct bufferevent_openssl *bev) +{ + struct evbuffer *input = bev->bev.bev.input; + struct event_watermark *wm = &bev->bev.bev.wm_read; + int result = READ_DEFAULT; + ev_ssize_t limit; + /* XXX 99% of this is generic code that nearly all bufferevents will + * want. */ + + if (bev->write_blocked_on_read) { + return 0; + } + + if (! (bev->bev.bev.enabled & EV_READ)) { + return 0; + } + + if (bev->bev.read_suspended) { + return 0; + } + + if (wm->high) { + if (evbuffer_get_length(input) >= wm->high) { + return 0; + } + + result = wm->high - evbuffer_get_length(input); + } else { + result = READ_DEFAULT; + } + + /* Respect the rate limit */ + limit = _bufferevent_get_read_max(&bev->bev); + if (result > limit) { + result = limit; + } + + return result; +} + + /* Things look readable. If write is blocked on read, write till it isn't. * Read from the underlying buffer until we block or we hit our high-water * mark. @@ -712,8 +756,7 @@ static void consider_reading(struct bufferevent_openssl *bev_ssl) { int r; - struct evbuffer *input = bev_ssl->bev.bev.input; - struct event_watermark *wm = &bev_ssl->bev.bev.wm_read; + int n_to_read; while (bev_ssl->write_blocked_on_read) { r = do_write(bev_ssl, WRITE_FRAME); @@ -722,15 +765,39 @@ consider_reading(struct bufferevent_openssl *bev_ssl) } if (bev_ssl->write_blocked_on_read) return; - while ((bev_ssl->bev.bev.enabled & EV_READ) && - (! bev_ssl->bev.read_suspended) && - (! wm->high || evbuffer_get_length(input) < wm->high)) { - int n_to_read = - wm->high ? wm->high - evbuffer_get_length(input) - : READ_DEFAULT; - r = do_read(bev_ssl, n_to_read); - if (r <= 0) + + n_to_read = bytes_to_read(bev_ssl); + + while (n_to_read) { + if (do_read(bev_ssl, n_to_read) <= 0) break; + + /* Read all pending data. This won't hit the network + * again, and will (most importantly) put us in a state + * where we don't need to read anything else until the + * socket is readable again. It'll potentially make us + * overrun our read high-watermark (somewhat + * regrettable). The damage to the rate-limit has + * already been done, since OpenSSL went and read a + * whole SSL record anyway. */ + n_to_read = SSL_pending(bev_ssl->ssl); + + /* XXX This if statement is actually a bad bug, added to avoid + * XXX a worse bug. + * + * The bad bug: It can potentially cause resource unfairness + * by reading too much data from the underlying bufferevent; + * it can potentially cause read looping if the underlying + * bufferevent is a bufferevent_pair and deferred callbacks + * aren't used. + * + * The worse bug: If we didn't do this, then we would + * potentially not read any more from bev_ssl->underlying + * until more data arrived there, which could lead to us + * waiting forever. + */ + if (!n_to_read && bev_ssl->underlying) + n_to_read = bytes_to_read(bev_ssl); } if (!bev_ssl->underlying) { @@ -814,6 +881,9 @@ be_openssl_eventcb(struct bufferevent *bev_base, short what, void *ctx) } else if (what & BEV_EVENT_TIMEOUT) { /* We sure didn't set this. Propagate it to the user. */ event = what; + } else if (what & BEV_EVENT_ERROR) { + /* An error occurred on the connection. Propagate it to the user. */ + event = what; } else if (what & BEV_EVENT_CONNECTED) { /* Ignore it. We're saying SSL_connect() already, which will eat it. */ @@ -1013,6 +1083,7 @@ be_openssl_outbuf_cb(struct evbuffer *buf, consider_writing(bev_ssl); } /* XXX Handle r < 0 */ + (void)r; } @@ -1163,6 +1234,7 @@ be_openssl_ctrl(struct bufferevent *bev, return -1; data->ptr = bev_ssl->underlying; return 0; + case BEV_CTRL_CANCEL_ALL: default: return -1; } @@ -1244,6 +1316,7 @@ bufferevent_openssl_new_impl(struct event_base *base, } if (underlying) { + bufferevent_setwatermark(underlying, EV_READ, 0, 0); bufferevent_enable(underlying, EV_READ|EV_WRITE); if (state == BUFFEREVENT_SSL_OPEN) bufferevent_suspend_read(underlying,