tls_socket: fix handling of syscall errors This prevents a case where we'd see an error like 'TlsSocket::Recv: Success' when trying to read from a TLS socket which got abruptly shut down by the remote side.
Change-Id: I9a0a63f861d71bd3186567ff98148476795530ab Reviewed-on: http://gerrit.cloudera.org:8080/5954 Tested-by: Kudu Jenkins Reviewed-by: Dan Burkert <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/kudu/repo Commit: http://git-wip-us.apache.org/repos/asf/kudu/commit/4a5ab736 Tree: http://git-wip-us.apache.org/repos/asf/kudu/tree/4a5ab736 Diff: http://git-wip-us.apache.org/repos/asf/kudu/diff/4a5ab736 Branch: refs/heads/master Commit: 4a5ab7368c9c3be39779d44f6bf701b1a925ce09 Parents: 91c93d3 Author: Todd Lipcon <[email protected]> Authored: Thu Feb 9 00:30:08 2017 -0800 Committer: Todd Lipcon <[email protected]> Committed: Wed Feb 15 18:24:51 2017 +0000 ---------------------------------------------------------------------- src/kudu/security/tls_socket.cc | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/kudu/blob/4a5ab736/src/kudu/security/tls_socket.cc ---------------------------------------------------------------------- diff --git a/src/kudu/security/tls_socket.cc b/src/kudu/security/tls_socket.cc index b510906..77f633f 100644 --- a/src/kudu/security/tls_socket.cc +++ b/src/kudu/security/tls_socket.cc @@ -24,6 +24,7 @@ #include "kudu/gutil/basictypes.h" #include "kudu/security/cert.h" #include "kudu/security/openssl_util.h" +#include "kudu/util/errno.h" namespace kudu { namespace security { @@ -58,7 +59,8 @@ Status TlsSocket::Write(const uint8_t *buf, int32_t amt, int32_t *nwritten) { *nwritten = 0; return Status::OK(); } - return Status::NetworkError("TlsSocket::Write", GetSSLErrorDescription(error_code)); + return Status::NetworkError("failed to write to TLS socket", + GetSSLErrorDescription(error_code)); } *nwritten = bytes_written; return Status::OK(); @@ -84,12 +86,16 @@ Status TlsSocket::Writev(const struct ::iovec *iov, int iov_len, int32_t *nwritt } Status TlsSocket::Recv(uint8_t *buf, int32_t amt, int32_t *nread) { + const char* kErrString = "failed to read from TLS socket"; + CHECK(ssl_); ERR_clear_error(); + errno = 0; int32_t bytes_read = SSL_read(ssl_.get(), buf, amt); + int save_errno = errno; if (bytes_read <= 0) { if (bytes_read == 0 && SSL_get_shutdown(ssl_.get()) == SSL_RECEIVED_SHUTDOWN) { - return Status::NetworkError("TlsSocket::Recv", "received remote shutdown", ESHUTDOWN); + return Status::NetworkError(kErrString, ErrnoToString(ESHUTDOWN), ESHUTDOWN); } auto error_code = SSL_get_error(ssl_.get(), bytes_read); if (error_code == SSL_ERROR_WANT_READ) { @@ -97,7 +103,24 @@ Status TlsSocket::Recv(uint8_t *buf, int32_t amt, int32_t *nread) { *nread = 0; return Status::OK(); } - return Status::NetworkError("TlsSocket::Recv", GetSSLErrorDescription(error_code)); + if (error_code == SSL_ERROR_SYSCALL && ERR_peek_error() == 0) { + // From the OpenSSL docs: + // Some I/O error occurred. The OpenSSL error queue may contain more + // information on the error. If the error queue is empty (i.e. + // ERR_get_error() returns 0), ret can be used to find out more about + // the error: If ret == 0, an EOF was observed that violates the pro- + // tocol. If ret == -1, the underlying BIO reported an I/O error (for + // socket I/O on Unix systems, consult errno for details). + if (bytes_read == 0) { + // "EOF was observed that violates the protocol" (eg the other end disconnected) + return Status::NetworkError(kErrString, ErrnoToString(ECONNRESET), ECONNRESET); + } + if (bytes_read == -1 && save_errno != 0) { + return Status::NetworkError(kErrString, ErrnoToString(save_errno), save_errno); + } + return Status::NetworkError(kErrString, "unknown ERROR_SYSCALL"); + } + return Status::NetworkError(kErrString, GetSSLErrorDescription(error_code)); } *nread = bytes_read; return Status::OK();
