From f71a728225feb44943feb609c08b301660344fc8 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Sat, 13 Jan 2018 17:53:59 -0800 Subject: [PATCH] ircd::net: Fix bug where received data is stuck in SSL buffer. --- ircd/net.cc | 80 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 33 deletions(-) diff --git a/ircd/net.cc b/ircd/net.cc index 63165aa91..66013466e 100644 --- a/ircd/net.cc +++ b/ircd/net.cc @@ -1389,6 +1389,37 @@ ircd::net::socket::wait(const wait_opts &opts, }); } +/// Asynchronous callback when the socket is ready +/// +/// Overload for operator() without a timeout. see: operator() +/// +void +ircd::net::socket::wait(const wait_opts &opts) +{ + const scope_timeout timeout + { + *this, opts.timeout + }; + + switch(opts.type) + { + case ready::ERROR: + sd.async_wait(wait_type::wait_error, yield_context{to_asio{}}); + break; + + case ready::WRITE: + sd.async_wait(wait_type::wait_write, yield_context{to_asio{}}); + break; + + case ready::READ: + sd.async_wait(wait_type::wait_read, yield_context{to_asio{}}); + break; + + default: + throw ircd::not_implemented{}; + } +} + /// Asynchronous callback when the socket is ready /// /// This function calls back the handler when the socket is ready @@ -1421,13 +1452,26 @@ ircd::net::socket::wait(const wait_opts &opts, case ready::READ: { + static char buf[1] alignas(16); + static const ilist bufs{buf}; + __builtin_prefetch(buf, 1, 0); // 1 = write, 0 = no cache + + // The problem here is that waiting on the sd doesn't account for bytes + // read into SSL that we didn't consume yet. If something is stuck in + // those userspace buffers, the socket won't know about it and perform + // the wait. ASIO should fix this by adding a ssl::stream.wait() method + // which will bail out immediately in this case before passing up to the + // real socket wait. + if(SSL_peek(ssl.native_handle(), buf, sizeof(buf)) > 0) + { + handle(error_code{}); + break; + } + // The problem here is that the wait operation gives ec=success on both a // socket error and when data is actually available. We then have to check // using a non-blocking peek in the handler. By doing it this way here we // just get the error in the handler's ec. - static char buf[16] alignas(16); - static const ilist bufs{buf}; - __builtin_prefetch(buf, 1, 0); // 1 = write, 0 = no cache sd.async_receive(bufs, sd.message_peek, std::move(handle)); //sd.async_wait(wait_type::wait_read, std::move(handle)); break; @@ -1438,36 +1482,6 @@ ircd::net::socket::wait(const wait_opts &opts, } } -/// Asynchronous callback when the socket is ready -/// -/// Overload for operator() without a timeout. see: operator() -/// -void -ircd::net::socket::wait(const wait_opts &opts) -{ - const scope_timeout timeout - { - *this, opts.timeout - }; - - switch(opts.type) - { - case ready::ERROR: - sd.async_wait(wait_type::wait_error, yield_context{to_asio{}}); - break; - - case ready::WRITE: - sd.async_wait(wait_type::wait_write, yield_context{to_asio{}}); - break; - - case ready::READ: - sd.async_wait(wait_type::wait_read, yield_context{to_asio{}}); - - default: - throw ircd::not_implemented{}; - } -} - void ircd::net::socket::handle_ready(const std::weak_ptr wp, const net::ready type,