summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoe <joe@61a7d7f5-40b7-0310-9c16-bb0ea8cb1845>2018-09-25 15:19:29 +0000
committerjoe <joe@61a7d7f5-40b7-0310-9c16-bb0ea8cb1845>2018-09-25 15:19:29 +0000
commita33ec92f2ecf02765cb3b8f64dc863bb9260413a (patch)
tree7ecaebfa02e4186d9be0a7271d53a75cee748428
parent6b892dd2fea7a5fd76857a3c0bf9f207793ab825 (diff)
downloadneon-a33ec92f2ecf02765cb3b8f64dc863bb9260413a.tar.gz
* src/ne_socket.c (error_ossl): Return NE_SOCK_RETRY for
SSL_ERROR_WANT_READ. (read_ossl, readable_ossl): Update for OpenSSL 1.1.1, block/timeout properly even if handshake data is received. git-svn-id: http://svn.webdav.org/repos/projects/neon/trunk@2025 61a7d7f5-40b7-0310-9c16-bb0ea8cb1845
-rw-r--r--src/ne_socket.c64
1 files changed, 57 insertions, 7 deletions
diff --git a/src/ne_socket.c b/src/ne_socket.c
index 406ed0b..999b63a 100644
--- a/src/ne_socket.c
+++ b/src/ne_socket.c
@@ -179,6 +179,9 @@ typedef struct in_addr ne_inet_addr;
/* Socket read timeout */
#define SOCKET_READ_TIMEOUT 120
+/* Internal read retry value */
+#define NE_SOCK_RETRY (-6)
+
/* Critical I/O functions on a socket: useful abstraction for easily
* handling SSL I/O alongside raw socket I/O. */
struct iofns {
@@ -599,13 +602,50 @@ static ssize_t writev_dummy(ne_socket *sock, const struct ne_iovec *vector, int
static const struct iofns iofns_raw = { read_raw, write_raw, readable_raw, writev_raw };
+static int error_ossl(ne_socket *sock, int sret);
+
#ifdef HAVE_OPENSSL
/* OpenSSL I/O function implementations. */
static int readable_ossl(ne_socket *sock, int secs)
{
+#if OPENSSL_VERSION_NUMBER < 0x10101000L
+ /* Sufficient for TLSv1.2 and earlier. */
if (SSL_pending(sock->ssl))
return 0;
return readable_raw(sock, secs);
+#else
+ /* TLSv1.3 sends a lot more handshake data so the presence of data
+ * on the socket - i.e. poll() returning 1, is an insufficient
+ * test for app-data readability. */
+ char pending;
+ int ret;
+ size_t bytes;
+
+ /* Loop while no app data is pending, each time attempting a one
+ * byte peek, and retrying the poll if that fails due to absence
+ * of app data. */
+ while (!SSL_pending(sock->ssl)) {
+ ret = readable_raw(sock, secs);
+ if (ret == NE_SOCK_TIMEOUT) {
+ return ret;
+ }
+
+ ret = SSL_peek_ex(sock->ssl, &pending, 1, &bytes);
+ if (ret) {
+ /* App data definitely available. */
+ break;
+ }
+ else {
+ /* If this gave SSL_ERROR_WANT_READ, loop and probably
+ * block again, else some other error happened. */
+ ret = error_ossl(sock, ret);
+ if (ret != NE_SOCK_RETRY)
+ return ret;
+ }
+ }
+
+ return 0;
+#endif /* OPENSSL_VERSION_NUMBER < 1.1.1 */
}
/* SSL error handling, according to SSL_get_error(3). */
@@ -618,6 +658,10 @@ static int error_ossl(ne_socket *sock, int sret)
set_error(sock, _("Connection closed"));
return NE_SOCK_CLOSED;
}
+ else if (errnum == SSL_ERROR_WANT_READ) {
+ set_error(sock, _("Retry operation"));
+ return NE_SOCK_RETRY;
+ }
/* for all other errors, look at the OpenSSL error stack */
err = ERR_get_error();
@@ -656,13 +700,15 @@ static ssize_t read_ossl(ne_socket *sock, char *buffer, size_t len)
{
int ret;
- ret = readable_ossl(sock, sock->rdtimeout);
- if (ret) return ret;
-
- ret = SSL_read(sock->ssl, buffer, CAST2INT(len));
- if (ret <= 0)
- ret = error_ossl(sock, ret);
-
+ do {
+ ret = readable_ossl(sock, sock->rdtimeout);
+ if (ret) return ret;
+
+ ret = SSL_read(sock->ssl, buffer, CAST2INT(len));
+ if (ret <= 0)
+ ret = error_ossl(sock, ret);
+ } while (ret == NE_SOCK_RETRY);
+
return ret;
}
@@ -1761,7 +1807,11 @@ int ne_sock_connect_ssl(ne_socket *sock, ne_ssl_context *ctx, void *userdata)
}
SSL_set_app_data(ssl, userdata);
+#if OPENSSL_VERSION_NUMBER < 0x10101000L
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
+#else
+ SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
+#endif
SSL_set_fd(ssl, sock->fd);
sock->ops = &iofns_ssl;