From: Azat Khuzhin <a3at.mail@gmail.com>
Date: Tue, 6 Dec 2016 23:53:07 +0000 (+0300)
Subject: be_openssl: Fix writing into filted openssl bufferevent after connected
X-Git-Tag: release-2.1.8-stable~5^2~2
X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8939676706b8e2a125c5e3344f8672ddfadeb4e1;p=libevent

be_openssl: Fix writing into filted openssl bufferevent after connected

The main problems was due to when bufferevent_openssl has underlying (i.e.
created with bufferevent_openssl_filter_new()) some events was
disabled/suspended, while with openssl, READ can require WRITE and vice-versa
hence this issues.

The BEV_CTRL_GET_FD hunk to fix http subsystem, since it depends from what
bufferevent_getfd() returns.

Fixes: #428
Fixes: ssl/bufferevent_filter_write_after_connect
Fixes: http/https_filter_chunk_out
Fixes: da52933550fd4736aa1c213b6de497e2ffc31e34 ("be_openssl: don't call
do_write() directly from outbuf_cb")
---

diff --git a/bufferevent_openssl.c b/bufferevent_openssl.c
index cbb7f8a5..da3963af 100644
--- a/bufferevent_openssl.c
+++ b/bufferevent_openssl.c
@@ -404,7 +404,10 @@ start_writing(struct bufferevent_openssl *bev_ssl)
 {
 	int r = 0;
 	if (bev_ssl->underlying) {
-		;
+		if (bev_ssl->write_blocked_on_read) {
+			bufferevent_unsuspend_read_(bev_ssl->underlying,
+			    BEV_SUSPEND_FILT_READ);
+		}
 	} else {
 		struct bufferevent *bev = &bev_ssl->bev.bev;
 		r = bufferevent_add_event_(&bev->ev_write, &bev->timeout_write);
@@ -435,7 +438,8 @@ stop_writing(struct bufferevent_openssl *bev_ssl)
 	if (bev_ssl->read_blocked_on_write)
 		return;
 	if (bev_ssl->underlying) {
-		;
+		bufferevent_unsuspend_read_(bev_ssl->underlying,
+		    BEV_SUSPEND_FILT_READ);
 	} else {
 		struct bufferevent *bev = &bev_ssl->bev.bev;
 		event_del(&bev->ev_write);
@@ -716,7 +720,7 @@ do_write(struct bufferevent_openssl *bev_ssl, int atmost)
 		if (bev_ssl->underlying)
 			BEV_RESET_GENERIC_WRITE_TIMEOUT(bev);
 
-		bufferevent_trigger_nolock_(bev, EV_WRITE, 0);
+		bufferevent_trigger_nolock_(bev, EV_WRITE, BEV_OPT_DEFER_CALLBACKS);
 	}
 	return result;
 }
@@ -1040,17 +1044,11 @@ do_handshake(struct bufferevent_openssl *bev_ssl)
 		print_err(err);
 		switch (err) {
 		case SSL_ERROR_WANT_WRITE:
-			if (!bev_ssl->underlying) {
-				stop_reading(bev_ssl);
-				return start_writing(bev_ssl);
-			}
-			return 0;
+			stop_reading(bev_ssl);
+			return start_writing(bev_ssl);
 		case SSL_ERROR_WANT_READ:
-			if (!bev_ssl->underlying) {
-				stop_writing(bev_ssl);
-				return start_reading(bev_ssl);
-			}
-			return 0;
+			stop_writing(bev_ssl);
+			return start_reading(bev_ssl);
 		default:
 			conn_closed(bev_ssl, BEV_EVENT_READING, err, r);
 			return -1;
@@ -1086,6 +1084,13 @@ set_handshake_callbacks(struct bufferevent_openssl *bev_ssl, evutil_socket_t fd)
 		    be_openssl_handshakecb, be_openssl_handshakecb,
 		    be_openssl_eventcb,
 		    bev_ssl);
+
+		if (fd < 0)
+			return 0;
+
+		if (bufferevent_setfd(bev_ssl->underlying, fd))
+			return 1;
+
 		return do_handshake(bev_ssl);
 	} else {
 		struct bufferevent *bev = &bev_ssl->bev.bev;
@@ -1131,10 +1136,13 @@ be_openssl_outbuf_cb(struct evbuffer *buf,
 	int r = 0;
 	/* XXX need to hold a reference here. */
 
-	if (cbinfo->n_added && bev_ssl->state == BUFFEREVENT_SSL_OPEN &&
-	    cbinfo->orig_size == 0) {
-		r = bufferevent_add_event_(&bev_ssl->bev.bev.ev_write,
-		    &bev_ssl->bev.bev.timeout_write);
+	if (cbinfo->n_added && bev_ssl->state == BUFFEREVENT_SSL_OPEN) {
+		if (cbinfo->orig_size == 0)
+			r = bufferevent_add_event_(&bev_ssl->bev.bev.ev_write,
+			    &bev_ssl->bev.bev.timeout_write);
+
+		if (bev_ssl->underlying)
+			consider_writing(bev_ssl);
 	}
 	/* XXX Handle r < 0 */
 	(void)r;
@@ -1286,17 +1294,24 @@ be_openssl_ctrl(struct bufferevent *bev,
 	struct bufferevent_openssl *bev_ssl = upcast(bev);
 	switch (op) {
 	case BEV_CTRL_SET_FD:
-		if (bev_ssl->underlying)
-			return -1;
-		{
+		if (!bev_ssl->underlying) {
 			BIO *bio;
 			bio = BIO_new_socket(data->fd, 0);
 			SSL_set_bio(bev_ssl->ssl, bio, bio);
+		} else {
+			BIO *bio;
+			if (!(bio = BIO_new_bufferevent(bev_ssl->underlying, 0)))
+				return -1;
+			SSL_set_bio(bev_ssl->ssl, bio, bio);
 		}
 
 		return be_openssl_set_fd(bev_ssl, bev_ssl->old_state, data->fd);
 	case BEV_CTRL_GET_FD:
-		data->fd = event_get_fd(&bev->ev_read);
+		if (bev_ssl->underlying) {
+			data->fd = event_get_fd(&bev_ssl->underlying->ev_read);
+		} else {
+			data->fd = event_get_fd(&bev->ev_read);
+		}
 		return 0;
 	case BEV_CTRL_GET_UNDERLYING:
 		data->ptr = bev_ssl->underlying;