Commit f59a9c5c authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa

nghttpx: Disable TLS renegotiation properly

4ed4efc2 does not disable TLS renegotiation at all, if client keeps
rengotiations without sending application data. In this change,
we intercept the raw incoming data from the client and if it is a
renegotiation, drop the connection immediately.
parent 6f5e1662
...@@ -48,10 +48,6 @@ namespace { ...@@ -48,10 +48,6 @@ namespace {
void upstream_readcb(bufferevent *bev, void *arg) void upstream_readcb(bufferevent *bev, void *arg)
{ {
auto handler = reinterpret_cast<ClientHandler*>(arg); auto handler = reinterpret_cast<ClientHandler*>(arg);
if(handler->get_tls_renegotiation()) {
delete handler;
return;
}
int rv = handler->on_read(); int rv = handler->on_read();
if(rv != 0) { if(rv != 0) {
delete handler; delete handler;
...@@ -64,7 +60,7 @@ void upstream_writecb(bufferevent *bev, void *arg) ...@@ -64,7 +60,7 @@ void upstream_writecb(bufferevent *bev, void *arg)
{ {
auto handler = reinterpret_cast<ClientHandler*>(arg); auto handler = reinterpret_cast<ClientHandler*>(arg);
// We actually depend on write low-warter mark == 0. // We actually depend on write low-warter mark == 0.
if(evbuffer_get_length(bufferevent_get_output(bev)) > 0) { if(handler->get_pending_write_length() > 0) {
// Possibly because of deferred callback, we may get this callback // Possibly because of deferred callback, we may get this callback
// when the output buffer is not empty. // when the output buffer is not empty.
return; return;
...@@ -223,6 +219,19 @@ void upstream_http1_connhd_readcb(bufferevent *bev, void *arg) ...@@ -223,6 +219,19 @@ void upstream_http1_connhd_readcb(bufferevent *bev, void *arg)
} }
} // namespace } // namespace
namespace {
void tls_raw_readcb(evbuffer *buffer, const evbuffer_cb_info *info, void *arg)
{
auto handler = static_cast<ClientHandler*>(arg);
if(handler->get_tls_renegotiation()) {
if(LOG_ENABLED(INFO)) {
CLOG(INFO, handler) << "Close connection due to TLS renegotiation";
}
delete handler;
}
}
} // namespace
ClientHandler::ClientHandler(bufferevent *bev, int fd, SSL *ssl, ClientHandler::ClientHandler(bufferevent *bev, int fd, SSL *ssl,
const char *ipaddr) const char *ipaddr)
: ipaddr_(ipaddr), : ipaddr_(ipaddr),
...@@ -247,6 +256,8 @@ ClientHandler::ClientHandler(bufferevent *bev, int fd, SSL *ssl, ...@@ -247,6 +256,8 @@ ClientHandler::ClientHandler(bufferevent *bev, int fd, SSL *ssl,
if(ssl_) { if(ssl_) {
SSL_set_app_data(ssl_, reinterpret_cast<char*>(this)); SSL_set_app_data(ssl_, reinterpret_cast<char*>(this));
set_bev_cb(nullptr, upstream_writecb, upstream_eventcb); set_bev_cb(nullptr, upstream_writecb, upstream_eventcb);
auto input = bufferevent_get_input(bufferevent_get_underlying(bev_));
evbuffer_add_cb(input, tls_raw_readcb, this);
} else { } else {
// For non-TLS version, first create HttpsUpstream. It may be // For non-TLS version, first create HttpsUpstream. It may be
// upgraded to HTTP/2.0 through HTTP Upgrade or direct HTTP/2.0 // upgraded to HTTP/2.0 through HTTP Upgrade or direct HTTP/2.0
...@@ -266,11 +277,16 @@ ClientHandler::~ClientHandler() ...@@ -266,11 +277,16 @@ ClientHandler::~ClientHandler()
SSL_set_shutdown(ssl_, SSL_RECEIVED_SHUTDOWN); SSL_set_shutdown(ssl_, SSL_RECEIVED_SHUTDOWN);
SSL_shutdown(ssl_); SSL_shutdown(ssl_);
} }
auto underlying = bufferevent_get_underlying(bev_);
bufferevent_disable(bev_, EV_READ | EV_WRITE); bufferevent_disable(bev_, EV_READ | EV_WRITE);
bufferevent_free(bev_); bufferevent_free(bev_);
if(ssl_) { if(ssl_) {
SSL_free(ssl_); SSL_free(ssl_);
} }
if(underlying) {
bufferevent_disable(underlying, EV_READ | EV_WRITE);
bufferevent_free(underlying);
}
shutdown(fd_, SHUT_WR); shutdown(fd_, SHUT_WR);
close(fd_); close(fd_);
for(auto dconn : dconn_pool_) { for(auto dconn : dconn_pool_) {
...@@ -435,8 +451,12 @@ DownstreamConnection* ClientHandler::get_downstream_connection() ...@@ -435,8 +451,12 @@ DownstreamConnection* ClientHandler::get_downstream_connection()
size_t ClientHandler::get_pending_write_length() size_t ClientHandler::get_pending_write_length()
{ {
auto output = bufferevent_get_output(bev_); auto underlying = bufferevent_get_underlying(bev_);
return evbuffer_get_length(output); auto len = evbuffer_get_length(bufferevent_get_output(bev_));
if(underlying) {
len += evbuffer_get_length(bufferevent_get_output(underlying));
}
return len;
} }
SSL* ClientHandler::get_ssl() const SSL* ClientHandler::get_ssl() const
......
...@@ -59,7 +59,8 @@ ssize_t send_callback(nghttp2_session *session, ...@@ -59,7 +59,8 @@ ssize_t send_callback(nghttp2_session *session,
auto bev = handler->get_bev(); auto bev = handler->get_bev();
auto output = bufferevent_get_output(bev); auto output = bufferevent_get_output(bev);
// Check buffer length and return WOULDBLOCK if it is large enough. // Check buffer length and return WOULDBLOCK if it is large enough.
if(evbuffer_get_length(output) > SHRPX_HTTP2_UPSTREAM_OUTPUT_UPPER_THRES) { if(handler->get_pending_write_length() >
SHRPX_HTTP2_UPSTREAM_OUTPUT_UPPER_THRES) {
return NGHTTP2_ERR_WOULDBLOCK; return NGHTTP2_ERR_WOULDBLOCK;
} }
...@@ -594,7 +595,7 @@ int Http2Upstream::on_read() ...@@ -594,7 +595,7 @@ int Http2Upstream::on_read()
} }
if(nghttp2_session_want_read(session_) == 0 && if(nghttp2_session_want_read(session_) == 0 &&
nghttp2_session_want_write(session_) == 0 && nghttp2_session_want_write(session_) == 0 &&
evbuffer_get_length(bufferevent_get_output(bev)) == 0) { handler_->get_pending_write_length() == 0) {
if(LOG_ENABLED(INFO)) { if(LOG_ENABLED(INFO)) {
ULOG(INFO, this) << "No more read/write for this HTTP2 session"; ULOG(INFO, this) << "No more read/write for this HTTP2 session";
} }
...@@ -619,7 +620,7 @@ int Http2Upstream::send() ...@@ -619,7 +620,7 @@ int Http2Upstream::send()
if(rv == 0) { if(rv == 0) {
if(nghttp2_session_want_read(session_) == 0 && if(nghttp2_session_want_read(session_) == 0 &&
nghttp2_session_want_write(session_) == 0 && nghttp2_session_want_write(session_) == 0 &&
evbuffer_get_length(bufferevent_get_output(handler_->get_bev())) == 0) { handler_->get_pending_write_length() == 0) {
if(LOG_ENABLED(INFO)) { if(LOG_ENABLED(INFO)) {
ULOG(INFO, this) << "No more read/write for this HTTP2 session"; ULOG(INFO, this) << "No more read/write for this HTTP2 session";
} }
......
...@@ -446,10 +446,8 @@ void https_downstream_readcb(bufferevent *bev, void *ptr) ...@@ -446,10 +446,8 @@ void https_downstream_readcb(bufferevent *bev, void *ptr)
} }
} }
} else { } else {
auto handler = upstream->get_client_handler(); if(upstream->get_client_handler()->get_pending_write_length() >
auto bev = handler->get_bev(); SHRPX_HTTPS_UPSTREAM_OUTPUT_UPPER_THRES) {
size_t outputlen = evbuffer_get_length(bufferevent_get_output(bev));
if(outputlen > SHRPX_HTTPS_UPSTREAM_OUTPUT_UPPER_THRES) {
downstream->pause_read(SHRPX_NO_BUFFER); downstream->pause_read(SHRPX_NO_BUFFER);
} }
} }
......
...@@ -59,7 +59,8 @@ ssize_t send_callback(spdylay_session *session, ...@@ -59,7 +59,8 @@ ssize_t send_callback(spdylay_session *session,
auto bev = handler->get_bev(); auto bev = handler->get_bev();
auto output = bufferevent_get_output(bev); auto output = bufferevent_get_output(bev);
// Check buffer length and return WOULDBLOCK if it is large enough. // Check buffer length and return WOULDBLOCK if it is large enough.
if(evbuffer_get_length(output) > SHRPX_SPDY_UPSTREAM_OUTPUT_UPPER_THRES) { if(handler->get_pending_write_length() >
SHRPX_SPDY_UPSTREAM_OUTPUT_UPPER_THRES) {
return SPDYLAY_ERR_WOULDBLOCK; return SPDYLAY_ERR_WOULDBLOCK;
} }
...@@ -451,7 +452,7 @@ int SpdyUpstream::on_read() ...@@ -451,7 +452,7 @@ int SpdyUpstream::on_read()
if(rv == 0) { if(rv == 0) {
if(spdylay_session_want_read(session_) == 0 && if(spdylay_session_want_read(session_) == 0 &&
spdylay_session_want_write(session_) == 0 && spdylay_session_want_write(session_) == 0 &&
evbuffer_get_length(bufferevent_get_output(handler_->get_bev())) == 0) { handler_->get_pending_write_length() == 0) {
if(LOG_ENABLED(INFO)) { if(LOG_ENABLED(INFO)) {
ULOG(INFO, this) << "No more read/write for this SPDY session"; ULOG(INFO, this) << "No more read/write for this SPDY session";
} }
...@@ -477,7 +478,7 @@ int SpdyUpstream::send() ...@@ -477,7 +478,7 @@ int SpdyUpstream::send()
if(rv == 0) { if(rv == 0) {
if(spdylay_session_want_read(session_) == 0 && if(spdylay_session_want_read(session_) == 0 &&
spdylay_session_want_write(session_) == 0 && spdylay_session_want_write(session_) == 0 &&
evbuffer_get_length(bufferevent_get_output(handler_->get_bev())) == 0) { handler_->get_pending_write_length() == 0) {
if(LOG_ENABLED(INFO)) { if(LOG_ENABLED(INFO)) {
ULOG(INFO, this) << "No more read/write for this SPDY session"; ULOG(INFO, this) << "No more read/write for this SPDY session";
} }
......
...@@ -458,9 +458,14 @@ ClientHandler* accept_connection(event_base *evbase, SSL_CTX *ssl_ctx, ...@@ -458,9 +458,14 @@ ClientHandler* accept_connection(event_base *evbase, SSL_CTX *ssl_ctx,
<< ERR_error_string(ERR_get_error(), nullptr); << ERR_error_string(ERR_get_error(), nullptr);
return nullptr; return nullptr;
} }
bev = bufferevent_openssl_socket_new SSL_set_fd(ssl, fd);
(evbase, fd, ssl, // To detect TLS renegotiation and deal with it, we have to use
BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_DEFER_CALLBACKS); // filter-based OpenSSL bufferevent and set evbuffer callback by
// our own.
auto underlying_bev = bufferevent_socket_new(evbase, fd, 0);
bev = bufferevent_openssl_filter_new(evbase, underlying_bev, ssl,
BUFFEREVENT_SSL_ACCEPTING,
BEV_OPT_DEFER_CALLBACKS);
} else { } else {
bev = bufferevent_socket_new(evbase, fd, BEV_OPT_DEFER_CALLBACKS); bev = bufferevent_socket_new(evbase, fd, BEV_OPT_DEFER_CALLBACKS);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment