From 81fe1934af666f81b4e6dededba514c7c0b39faa Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 26 Aug 2011 16:10:17 -0400 Subject: [PATCH] Fix a bufferevent-related bug that killed tunneled dirserv conns Because tunneled connections are implemented with buffervent_pair, writing to them can cause an immediate flush. This means that added to them and then checking to see whether their outbuf is empty is _not_ an adequate way to see whether you added anything. This caused a problem in directory server connections, since they would try spooling a little more data out, and then close the connection if there was no queued data to send. This fix should improve matters; it only closes the connection if there is no more data to spool, and all of the spooling callbacks are supposed to put the dirconn into dir_spool_none on completion. This is bug 3814; Sebastian found it; bugfix on 0.2.3.1-alpha. --- changes/bug3814 | 4 ++++ src/or/directory.c | 17 +++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 changes/bug3814 diff --git a/changes/bug3814 b/changes/bug3814 new file mode 100644 index 0000000000..3db0e3ef75 --- /dev/null +++ b/changes/bug3814 @@ -0,0 +1,4 @@ + o Major bugfixes (bufferevents): + - Fix a bug where server-side tunneled bufferevent-based directory + streams would get closed prematurely. Fixes 3814, bugfix on + 0.2.3.1-alpha. diff --git a/src/or/directory.c b/src/or/directory.c index 744bc120fb..c3865eda85 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -3545,8 +3545,21 @@ connection_dir_finished_flushing(dir_connection_t *conn) conn->_base.state = DIR_CONN_STATE_CLIENT_READING; return 0; case DIR_CONN_STATE_SERVER_WRITING: - log_debug(LD_DIRSERV,"Finished writing server response. Closing."); - connection_mark_for_close(TO_CONN(conn)); + if (conn->dir_spool_src != DIR_SPOOL_NONE) { +#ifdef USE_BUFFEREVENTS + /* This can happen with paired bufferevents, since a paired connection + * can flush immediately when you write to it, making the subsequent + * check in connection_handle_write_cb() decide that the connection + * is flushed. */ + log_debug(LD_DIRSERV, "Emptied a dirserv buffer, but still spooling."); +#else + log_warn(LD_BUG, "Emptied a dirserv buffer, but it's still spooling!"); + connection_mark_for_close(TO_CONN(conn)); +#endif + } else { + log_debug(LD_DIRSERV, "Finished writing server response. Closing."); + connection_mark_for_close(TO_CONN(conn)); + } return 0; default: log_warn(LD_BUG,"called in unexpected state %d.",