Merge pull request #201 from cconlon/socketSelectEAGAIN

JNI/JSSE: Add support for poll(), fix for EAGAIN and select()
pull/206/head
JacobBarthelmeh 2024-07-11 15:12:09 -06:00 committed by GitHub
commit bef6379273
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 455 additions and 183 deletions

View File

@ -327,6 +327,24 @@ $ ./configure --enable-secure-renegotiation
Or by defining `-DHAVE_SECURE_RENEGOTIATION`.
### Native File Descriptor Events
wolfSSL JNI/JSSE internally makes several calls that operate on native
file descriptors inside Java Socket objects. These native file descriptors
are watched for read and write events with either `select()` or `poll()`.
By default `poll()` will be used, unless `WOLFJNI_USE_IO_SELECT` is defined
or added to CFLAGS when compiling the native JNI sources (see `java.sh`).
Windows builds will also default to using `select()` since `poll()` is not
available there.
wolfSSL JNI/JSSE does not select/poll on a large number of file descriptors
(typically just one). Although if used in applications that make lots of
connections, when using `select()` the `FD_ISSET` and other related macros
result in undefined behavior when the file descriptor number is larger than
`FD_SETSIZE` (defaults to 1024 on most systems). For this reason, `poll()` is
used as the default descriptor monitoring function.
## Release Notes
Release notes can be found in [ChangeLog.md](./ChangeLog.md).

View File

@ -9,8 +9,18 @@ extern "C" {
#endif
#undef com_wolfssl_WolfSSL_JNI_SESSION_UNAVAILABLE
#define com_wolfssl_WolfSSL_JNI_SESSION_UNAVAILABLE -10001L
#undef com_wolfssl_WolfSSL_WOLFJNI_TIMEOUT
#define com_wolfssl_WolfSSL_WOLFJNI_TIMEOUT -11L
#undef com_wolfssl_WolfSSL_WOLFJNI_IO_EVENT_FAIL
#define com_wolfssl_WolfSSL_WOLFJNI_IO_EVENT_FAIL -10L
#undef com_wolfssl_WolfSSL_WOLFJNI_IO_EVENT_TIMEOUT
#define com_wolfssl_WolfSSL_WOLFJNI_IO_EVENT_TIMEOUT -11L
#undef com_wolfssl_WolfSSL_WOLFJNI_IO_EVENT_ERROR
#define com_wolfssl_WolfSSL_WOLFJNI_IO_EVENT_ERROR -14L
#undef com_wolfssl_WolfSSL_WOLFJNI_IO_EVENT_FD_CLOSED
#define com_wolfssl_WolfSSL_WOLFJNI_IO_EVENT_FD_CLOSED -15L
#undef com_wolfssl_WolfSSL_WOLFJNI_IO_EVENT_POLLHUP
#define com_wolfssl_WolfSSL_WOLFJNI_IO_EVENT_POLLHUP -16L
#undef com_wolfssl_WolfSSL_WOLFJNI_IO_EVENT_INVALID_TIMEOUT
#define com_wolfssl_WolfSSL_WOLFJNI_IO_EVENT_INVALID_TIMEOUT -17L
#undef com_wolfssl_WolfSSL_SSL_ERROR_NONE
#define com_wolfssl_WolfSSL_SSL_ERROR_NONE 0L
#undef com_wolfssl_WolfSSL_SSL_FAILURE

View File

@ -33,6 +33,11 @@
#include <arpa/inet.h>
#include <sys/errno.h>
#endif
#ifdef WOLFJNI_USE_IO_SELECT
#include <sys/select.h>
#else
#include <poll.h>
#endif
#ifndef WOLFSSL_JNI_DEFAULT_PEEK_TIMEOUT
/* Default wolfSSL_peek() timeout for wolfSSL_get_session(), ms */
@ -186,9 +191,6 @@ int NativeSSLVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store)
return retval;
}
/* jni functions */
JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_newSSL
(JNIEnv* jenv, jobject jcl, jlong ctx)
{
@ -599,22 +601,50 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_getFd
return wolfSSL_get_fd(ssl);
}
/* enum values used in socketSelect() */
/* enum values used in socketSelect() and socketPoll(). Some of these
* values are also duplicated in WolfSSL.java for access from Java classes.
* If updated here, make sure to update in WolfSSL.java too. */
enum {
WOLFJNI_SELECT_FAIL = -10,
WOLFJNI_TIMEOUT = -11, /* also in WolfSSL.java */
WOLFJNI_RECV_READY = -12,
WOLFJNI_SEND_READY = -13,
WOLFJNI_ERROR_READY = -14
WOLFJNI_IO_EVENT_FAIL = -10,
WOLFJNI_IO_EVENT_TIMEOUT = -11,
WOLFJNI_IO_EVENT_RECV_READY = -12,
WOLFJNI_IO_EVENT_SEND_READY = -13,
WOLFJNI_IO_EVENT_ERROR = -14,
WOLFJNI_IO_EVENT_FD_CLOSED = -15,
WOLFJNI_IO_EVENT_POLLHUP = -16,
WOLFJNI_IO_EVENT_INVALID_TIMEOUT = -17
};
/* perform a select() call on underlying socket to wait for socket to be ready
* to read/write, or timeout. Note that we explicitly set the underlying
* socket descriptor to non-blocking so we can select() on it.
#ifdef WOLFJNI_USE_IO_SELECT
/* Perform a select() call on the underlying socket to wait for socket to be
* ready for read/write, or timeout. Note that we explicitly set the underlying
* socket descriptor to non-blocking.
*
* The Java socket timeout value representing no timeout is NULL, not 0 like
* C. We adjust for this when handling timeout_ms here. timeout_ms is in
* milliseconds. */
* NOTE: the FD_ISSET macro behavior is undefined if the descriptor value is
* less than 0 or greater than or equal to FD_SETSIZE (1024 by default).
*
* On a Java Socket, a timeout of 0 is an infinite timeout. Greater than zero
* is a timeout in milliseconds. Negative timeout is invalid and not supported.
* For select(), a non-NULL timeval struct specifies maximum timeout to wait,
* a NULL timeval struct is an infinite timeout. A zero-valued timeval struct
* will return immediately (no timeout).
*
* @param sockfd socket descriptor to select()
* @param timeout_ms timeout in milliseconds. 0 indicates infinite timeout, to
* match Java timeout behavior. Negative timeout not
* supported, since not supported on Java Socket.
* @param rx set to 1 to monitor readability on socket descriptor,
* otherwise 0 to monitor writability
*
* @return possible return values are:
* WOLFJNI_IO_EVENT_FAIL
* WOLFJNI_IO_EVENT_ERROR
* WOLFJNI_IO_EVENT_TIMEOUT
* WOLFJNI_IO_EVENT_RECV_READY
* WOLFJNI_IO_EVENT_SEND_READY
* WOLFJNI_IO_EVENT_INVALID_TIMEOUT
*/
static int socketSelect(int sockfd, int timeout_ms, int rx)
{
fd_set fds, errfds;
@ -624,6 +654,11 @@ static int socketSelect(int sockfd, int timeout_ms, int rx)
int result = 0;
struct timeval timeout;
/* Java Socket does not support negative timeouts, sanitize */
if (timeout_ms < 0) {
return WOLFJNI_IO_EVENT_INVALID_TIMEOUT;
}
#ifndef USE_WINDOWS_API
do {
#endif
@ -648,31 +683,118 @@ static int socketSelect(int sockfd, int timeout_ms, int rx)
}
if (result == 0) {
return WOLFJNI_TIMEOUT;
return WOLFJNI_IO_EVENT_TIMEOUT;
} else if (result > 0) {
if (FD_ISSET(sockfd, &fds)) {
if (rx) {
return WOLFJNI_RECV_READY;
return WOLFJNI_IO_EVENT_RECV_READY;
} else {
return WOLFJNI_SEND_READY;
return WOLFJNI_IO_EVENT_SEND_READY;
}
} else if (FD_ISSET(sockfd, &errfds)) {
return WOLFJNI_ERROR_READY;
return WOLFJNI_IO_EVENT_ERROR;
}
}
#ifndef USE_WINDOWS_API
} while ((result == -1) && (errno == EINTR));
} while ((result == -1) && ((errno == EINTR) || (errno == EAGAIN)));
#endif
/* Return on error, unless select() was interrupted, try again above */
return WOLFJNI_SELECT_FAIL;
/* Return on error, unless errno EINTR or EAGAIN, try again above */
return WOLFJNI_IO_EVENT_FAIL;
}
#else /* !WOLFJNI_USE_IO_SELECT */
/* Perform poll() on underlying socket descriptor to wait for socket to be
* ready for read/write, or timeout. Note that we are explicitly setting
* the underlying descriptor to non-blocking.
*
* On a Java Socket, a timeout of 0 is an infinite timeout. Greater than zero
* is a timeout in milliseconds. Negative timeout is invalid and not supported.
* For poll(), timeout greater than 0 specifies max timeout in milliseconds,
* zero timeout will return immediately (no timeout), and -1 will block
* indefinitely.
*
* @param sockfd socket descriptor to poll()
* @param timeout_ms timeout in milliseconds. 0 indicates infinite timeout, to
* match Java timeout behavior. Negative timeout not
* supported, since not supported on Java Socket.
* @param rx set to 1 to monitor readability on socket descriptor,
* otherwise 0 to ignore readability events
* @param tx set to 1 to monitor writability on socket descriptor,
* otherwise 0 to ignore writability events
*
* @return possible return values are:
* WOLFJNI_IO_EVENT_FAIL
* WOLFJNI_IO_EVENT_ERROR
* WOLFJNI_IO_EVENT_TIMEOUT
* WOLFJNI_IO_EVENT_RECV_READY
* WOLFJNI_IO_EVENT_SEND_READY
* WOLFJNI_IO_EVENT_FD_CLOSED
* WOLFJNI_IO_EVENT_POLLHUP
* WOLFJNI_IO_EVENT_INVALID_TIMEOUT
*/
static int socketPoll(int sockfd, int timeout_ms, int rx, int tx)
{
int ret;
int timeout;
struct pollfd fds[1];
/* Sanitize timeout and convert from Java to poll() expectations */
timeout = timeout_ms;
if (timeout < 0) {
return WOLFJNI_IO_EVENT_INVALID_TIMEOUT;
} else if (timeout == 0) {
timeout = -1;
}
fds[0].fd = sockfd;
fds[0].events = 0;
if (tx) {
fds[0].events |= POLLOUT;
}
if (rx) {
fds[0].events |= POLLIN;
}
do {
ret = poll(fds, 1, timeout);
if (ret == 0) {
return WOLFJNI_IO_EVENT_TIMEOUT;
} else if (ret > 0) {
if (fds[0].revents & POLLIN ||
fds[0].revents & POLLPRI) { /* read possible */
return WOLFJNI_IO_EVENT_RECV_READY;
} else if (fds[0].revents & POLLOUT) { /* write possible */
return WOLFJNI_IO_EVENT_SEND_READY;
} else if (fds[0].revents & POLLNVAL) { /* fd not open */
return WOLFJNI_IO_EVENT_FD_CLOSED;
} else if (fds[0].revents & POLLERR) { /* exceptional error */
return WOLFJNI_IO_EVENT_ERROR;
} else if (fds[0].revents & POLLHUP) { /* sock disconnected */
return WOLFJNI_IO_EVENT_POLLHUP;
}
}
} while ((ret == -1) && ((errno == EINTR) || (errno == EAGAIN)));
return WOLFJNI_IO_EVENT_FAIL;
}
#endif /* WOLFJNI_USE_IO_SELECT */
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_connect
(JNIEnv* jenv, jobject jcl, jlong sslPtr, jint timeout)
{
int ret = 0, err = 0, sockfd = 0;
int pollRx = 0;
int pollTx = 0;
wolfSSL_Mutex* jniSessLock = NULL;
SSLAppData* appData = NULL;
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
@ -726,12 +848,28 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_connect
break;
}
ret = socketSelect(sockfd, (int)timeout, 1);
if (ret == WOLFJNI_RECV_READY || ret == WOLFJNI_SEND_READY) {
if (err == SSL_ERROR_WANT_READ) {
pollRx = 1;
}
else if (err == SSL_ERROR_WANT_WRITE) {
pollTx = 1;
}
#if defined(WOLFJNI_USE_IO_SELECT) || defined(USE_WINDOWS_API)
ret = socketSelect(sockfd, (int)timeout, pollRx);
#else
ret = socketPoll(sockfd, (int)timeout, pollRx, pollTx);
#endif
if ((ret == WOLFJNI_IO_EVENT_RECV_READY) ||
(ret == WOLFJNI_IO_EVENT_SEND_READY)) {
/* I/O ready, continue handshake and try again */
continue;
} else if (ret == WOLFJNI_TIMEOUT) {
/* Java will throw SocketTimeoutException */
} else if (ret == WOLFJNI_IO_EVENT_TIMEOUT ||
ret == WOLFJNI_IO_EVENT_FD_CLOSED ||
ret == WOLFJNI_IO_EVENT_ERROR ||
ret == WOLFJNI_IO_EVENT_POLLHUP ||
ret == WOLFJNI_IO_EVENT_FAIL) {
/* Java will throw SocketTimeoutException or SocketException */
break;
} else {
/* error */
@ -758,6 +896,8 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_write
{
byte* data = NULL;
int ret = SSL_FAILURE, err, sockfd;
int pollRx = 0;
int pollTx = 0;
wolfSSL_Mutex* jniSessLock = NULL;
SSLAppData* appData = NULL;
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
@ -819,12 +959,32 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_write
break;
}
ret = socketSelect(sockfd, (int)timeout, 0);
if (ret == WOLFJNI_RECV_READY || ret == WOLFJNI_SEND_READY) {
if (err == SSL_ERROR_WANT_READ) {
pollRx = 1;
}
else if (err == SSL_ERROR_WANT_WRITE) {
pollTx = 1;
}
#if defined(WOLFJNI_USE_IO_SELECT) || defined(USE_WINDOWS_API)
ret = socketSelect(sockfd, (int)timeout, pollRx);
#else
ret = socketPoll(sockfd, (int)timeout, pollRx, pollTx);
#endif
if ((ret == WOLFJNI_IO_EVENT_RECV_READY) ||
(ret == WOLFJNI_IO_EVENT_SEND_READY)) {
/* loop around and try wolfSSL_write() again */
continue;
} else if (ret == WOLFJNI_IO_EVENT_TIMEOUT ||
ret == WOLFJNI_IO_EVENT_FD_CLOSED ||
ret == WOLFJNI_IO_EVENT_ERROR ||
ret == WOLFJNI_IO_EVENT_POLLHUP ||
ret == WOLFJNI_IO_EVENT_FAIL) {
/* Java will throw SocketTimeoutException or
* SocketException */
break;
} else {
/* error or timeout occurred during select */
/* error */
ret = WOLFSSL_FAILURE;
break;
}
@ -847,6 +1007,8 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_read
{
byte* data = NULL;
int size = 0, ret, err, sockfd;
int pollRx = 0;
int pollTx = 0;
wolfSSL_Mutex* jniSessLock = NULL;
SSLAppData* appData = NULL;
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
@ -905,12 +1067,32 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_read
break;
}
ret = socketSelect(sockfd, timeout, 1);
if (ret == WOLFJNI_RECV_READY || ret == WOLFJNI_SEND_READY) {
if (err == SSL_ERROR_WANT_READ) {
pollRx = 1;
}
else if (err == SSL_ERROR_WANT_WRITE) {
pollTx = 1;
}
#if defined(WOLFJNI_USE_IO_SELECT) || defined(USE_WINDOWS_API)
ret = socketSelect(sockfd, (int)timeout, pollRx);
#else
ret = socketPoll(sockfd, (int)timeout, pollRx, pollTx);
#endif
if ((ret == WOLFJNI_IO_EVENT_RECV_READY) ||
(ret == WOLFJNI_IO_EVENT_SEND_READY)) {
/* loop around and try wolfSSL_read() again */
continue;
} else if (ret == WOLFJNI_IO_EVENT_TIMEOUT ||
ret == WOLFJNI_IO_EVENT_FD_CLOSED ||
ret == WOLFJNI_IO_EVENT_ERROR ||
ret == WOLFJNI_IO_EVENT_POLLHUP ||
ret == WOLFJNI_IO_EVENT_FAIL) {
/* Java will throw SocketTimeoutException or
* SocketException */
break;
} else {
/* error or timeout occurred during select */
/* other error occurred */
size = ret;
break;
}
@ -930,6 +1112,8 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_accept
(JNIEnv* jenv, jobject jcl, jlong sslPtr, jint timeout)
{
int ret = 0, err, sockfd;
int pollRx = 0;
int pollTx = 0;
wolfSSL_Mutex* jniSessLock = NULL;
SSLAppData* appData = NULL;
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
@ -983,12 +1167,33 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_accept
break;
}
ret = socketSelect(sockfd, (int)timeout, 1);
if (ret == WOLFJNI_RECV_READY || ret == WOLFJNI_SEND_READY) {
/* I/O ready, continue handshake and try again */
if (err == SSL_ERROR_WANT_READ) {
pollRx = 1;
}
else if (err == SSL_ERROR_WANT_WRITE) {
pollTx = 1;
}
#if defined(WOLFJNI_USE_IO_SELECT) || defined(USE_WINDOWS_API)
ret = socketSelect(sockfd, (int)timeout, pollRx);
#else
ret = socketPoll(sockfd, (int)timeout, pollRx, pollTx);
#endif
if ((ret == WOLFJNI_IO_EVENT_RECV_READY) ||
(ret == WOLFJNI_IO_EVENT_SEND_READY)) {
/* loop around and try wolfSSL_accept() again */
continue;
} else if (ret == WOLFJNI_IO_EVENT_TIMEOUT ||
ret == WOLFJNI_IO_EVENT_FD_CLOSED ||
ret == WOLFJNI_IO_EVENT_ERROR ||
ret == WOLFJNI_IO_EVENT_POLLHUP ||
ret == WOLFJNI_IO_EVENT_FAIL) {
/* Java will throw SocketTimeoutException or
* SocketException */
break;
} else {
/* error or timeout */
/* other error occurred */
ret = SSL_FAILURE;
break;
}
}
@ -1156,6 +1361,8 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_shutdownSSL
(JNIEnv* jenv, jobject jcl, jlong sslPtr, jint timeout)
{
int ret = 0, err, sockfd;
int pollRx = 0;
int pollTx = 0;
wolfSSL_Mutex* jniSessLock;
SSLAppData* appData = NULL;
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
@ -1209,12 +1416,33 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_shutdownSSL
break;
}
ret = socketSelect(sockfd, timeout, 1);
if (ret == WOLFJNI_RECV_READY || ret == WOLFJNI_SEND_READY) {
/* I/O ready, continue handshake and try again */
if (err == SSL_ERROR_WANT_READ) {
pollRx = 1;
}
else if (err == SSL_ERROR_WANT_WRITE) {
pollTx = 1;
}
#if defined(WOLFJNI_USE_IO_SELECT) || defined(USE_WINDOWS_API)
ret = socketSelect(sockfd, (int)timeout, pollRx);
#else
ret = socketPoll(sockfd, (int)timeout, pollRx, pollTx);
#endif
if ((ret == WOLFJNI_IO_EVENT_RECV_READY) ||
(ret == WOLFJNI_IO_EVENT_SEND_READY)) {
/* loop around and try wolfSSL_shutdown() again */
continue;
} else if (ret == WOLFJNI_IO_EVENT_TIMEOUT ||
ret == WOLFJNI_IO_EVENT_FD_CLOSED ||
ret == WOLFJNI_IO_EVENT_ERROR ||
ret == WOLFJNI_IO_EVENT_POLLHUP ||
ret == WOLFJNI_IO_EVENT_FAIL) {
/* Java will throw SocketTimeoutException or
* SocketException */
break;
} else {
/* error or timeout */
/* other error occurred */
ret = SSL_FAILURE;
break;
}
}
@ -1401,9 +1629,16 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_get1Session
break;
}
#if defined(WOLFJNI_USE_IO_SELECT) || defined(USE_WINDOWS_API)
/* Default to select() on Windows or if WOLFJNI_USE_IO_SELECT */
ret = socketSelect(sockfd,
(int)WOLFSSL_JNI_DEFAULT_PEEK_TIMEOUT, 1);
if (ret == WOLFJNI_RECV_READY || ret == WOLFJNI_SEND_READY) {
#else
ret = socketPoll(sockfd,
(int)WOLFSSL_JNI_DEFAULT_PEEK_TIMEOUT, 1, 0);
#endif
if ((ret == WOLFJNI_IO_EVENT_RECV_READY) ||
(ret == WOLFJNI_IO_EVENT_SEND_READY)) {
/* I/O ready, continue handshake and try again */
continue;
} else {

View File

@ -54,9 +54,36 @@ public class WolfSSL {
public static final int JNI_SESSION_UNAVAILABLE = -10001;
/**
* Socket timed out, matches com_wolfssl_WolfSSLSession.c socketSelect()
* return value */
public static final int WOLFJNI_TIMEOUT = -11;
* Socket select/poll() failed, matches com_wolfssl_WolfSSLSession.c
* socketSelect() and socketPoll() return value.
*/
public static final int WOLFJNI_IO_EVENT_FAIL = -10;
/**
* Socket timed out, matches com_wolfssl_WolfSSLSession.c
* socketSelect() and socketPoll() return value.
*/
public static final int WOLFJNI_IO_EVENT_TIMEOUT = -11;
/**
* Socket poll() exceptional error, matches com_wolfssl_WolfSSLSession.c
* socketPoll() return value */
public static final int WOLFJNI_IO_EVENT_ERROR = -14;
/**
* Socket file descriptor closed, matches com_wolfssl_WolfSSLSession.c
* socketPoll() return value */
public static final int WOLFJNI_IO_EVENT_FD_CLOSED = -15;
/**
* Socket disconnected during poll(), matches
* com_wolfssl_WolfSSLSession.c socketPoll() return value */
public static final int WOLFJNI_IO_EVENT_POLLHUP = -16;
/**
* Socket invalid timeout during poll/select(), matches
* com_wolfssl_WolfSSLSession.c socketPoll/socketSelect() return value */
public static final int WOLFJNI_IO_EVENT_INVALID_TIMEOUT = -17;
/* ----------------------- wolfSSL codes ---------------------------- */

View File

@ -94,11 +94,6 @@ public class WolfSSLSession {
/* lock around native WOLFSSL pointer use */
private final Object sslLock = new Object();
/* return values from naitve socketSelect(), should match
* ones in native/com_wolfssl_WolfSSLSession.c */
private int WOLFJNI_SELECT_FAIL = -10;
private int WOLFJNI_TIMEOUT = -11;
/* SNI requested by this WolfSSLSession if client side and useSNI()
* was called successfully. */
private byte[] clientSNIRequested = null;
@ -567,6 +562,36 @@ public class WolfSSLSession {
}
}
/**
* Helper method to throw appropriate exception based on native
* result of poll()/select() from API that does I/O.
*/
private static void throwExceptionFromIOReturnValue(
int ret, String nativeFunc)
throws SocketTimeoutException, SocketException {
if (ret == WolfSSL.WOLFJNI_IO_EVENT_TIMEOUT) {
throw new SocketTimeoutException(
"Native socket timed out during " + nativeFunc);
}
else if (ret == WolfSSL.WOLFJNI_IO_EVENT_FD_CLOSED) {
throw new SocketException("Socket fd closed during poll(), " +
"errno = " + WolfSSL.getErrno());
}
else if (ret == WolfSSL.WOLFJNI_IO_EVENT_ERROR) {
throw new SocketException("Socket fd poll() exceptional error, " +
"errno = " + WolfSSL.getErrno());
}
else if (ret == WolfSSL.WOLFJNI_IO_EVENT_POLLHUP) {
throw new SocketException("Socket disconnected during poll(), " +
"errno = " + WolfSSL.getErrno());
}
else if (ret == WolfSSL.WOLFJNI_IO_EVENT_FAIL) {
throw new SocketException("Socket select/poll() failed, " +
"errno = " + WolfSSL.getErrno());
}
}
/**
* Initializes an SSL/TLS handshake with a server.
* This function is called on the client side. When called, the underlying
@ -602,29 +627,12 @@ public class WolfSSLSession {
* a more detailed error code, call <code>getError()</code>.
* @throws IllegalStateException WolfSSLContext has been freed
* @throws SocketTimeoutException if underlying socket timed out
* @throws SocketException Native socket select() failed
* @throws SocketException Native socket select() or poll() failed
*/
public int connect()
throws IllegalStateException, SocketTimeoutException, SocketException {
int ret = 0;
confirmObjectIsActive();
synchronized (sslLock) {
ret = connect(this.sslPtr, 0);
}
if (ret == WolfSSL.WOLFJNI_TIMEOUT) {
throw new SocketTimeoutException(
"Native socket timed out during SSL_connect()");
}
else if (ret == WOLFJNI_SELECT_FAIL) {
throw new SocketException("Socket select() failed, errno = " +
WolfSSL.getErrno());
}
return ret;
return connect(0);
}
/**
@ -657,14 +665,15 @@ public class WolfSSLSession {
* <p>
* before calling <code>newSSL()</code>, though it's not recommended.
*
* @param timeout read timeout, milliseconds.
* @param timeout read timeout, milliseconds. Specify 0 to use infinite
* timeout
*
* @return <code>SSL_SUCCESS</code> if successful, otherwise
* <code>SSL_FATAL_ERROR</code> if an error occurred. To get
* <code>SSL_FAILURE</code> if an error occurred. To get
* a more detailed error code, call <code>getError()</code>.
* @throws IllegalStateException WolfSSLContext has been freed
* @throws SocketTimeoutException if socket timeout occurs
* @throws SocketException Native socket select() failed
* @throws SocketException Native socket select() or poll() failed
*/
public int connect(int timeout)
throws IllegalStateException, SocketTimeoutException, SocketException {
@ -677,13 +686,7 @@ public class WolfSSLSession {
ret = connect(this.sslPtr, timeout);
}
if (ret == WOLFJNI_TIMEOUT) {
throw new SocketTimeoutException("Socket connect timeout");
}
else if (ret == WOLFJNI_SELECT_FAIL) {
throw new SocketException("Socket select() failed, errno = " +
WolfSSL.getErrno());
}
throwExceptionFromIOReturnValue(ret, "wolfSSL_connect()");
return ret;
}
@ -710,7 +713,7 @@ public class WolfSSLSession {
* @param length size, in bytes, of data to send to the peer
* @return the number of bytes written upon success. <code>0
* </code>or a negative value will be returned upon failure.
* <code>SSL_FATAL_ERROR</code>return upon failure when either
* <code>SSL_FAILURE</code>return upon failure when either
* an error occurred or, when using non-blocking sockets,
* the <b>SSL_ERROR_WANT_READ</b> or
* <b>SSL_ERROR_WANT_WRITE</b> error was received and the
@ -718,10 +721,11 @@ public class WolfSSLSession {
* <code>BAD_FUNC_ARC</code> when bad arguments are used.
* Use <code>getError</code> to get a specific error code.
* @throws IllegalStateException WolfSSLContext has been freed
* @throws SocketException Native socket select() failed
* @throws SocketTimeoutException if socket timeout occurs
* @throws SocketException Native socket select() or poll() failed
*/
public int write(byte[] data, int length)
throws IllegalStateException, SocketException {
throws IllegalStateException, SocketTimeoutException, SocketException {
int ret;
long localPtr;
@ -741,10 +745,7 @@ public class WolfSSLSession {
* occur if needed */
ret = write(localPtr, data, 0, length, 0);
if (ret == WOLFJNI_SELECT_FAIL) {
throw new SocketException("Socket select() failed, errno = " +
WolfSSL.getErrno());
}
throwExceptionFromIOReturnValue(ret, "wolfSSL_write()");
return ret;
}
@ -782,7 +783,7 @@ public class WolfSSLSession {
* Use <code>getError</code> to get a specific error code.
* @throws IllegalStateException WolfSSLContext has been freed
* @throws SocketTimeoutException if socket timeout occurs
* @throws SocketException Native socket select() failed
* @throws SocketException Native socket select/poll() failed
*/
public int write(byte[] data, int length, int timeout)
throws IllegalStateException, SocketTimeoutException, SocketException {
@ -826,7 +827,7 @@ public class WolfSSLSession {
* Use <code>getError</code> to get a specific error code.
* @throws IllegalStateException WolfSSLContext has been freed
* @throws SocketTimeoutException if socket timeout occurs
* @throws SocketException Native socket select() failed
* @throws SocketException Native socket select/poll() failed
*/
public int write(byte[] data, int offset, int length, int timeout)
throws IllegalStateException, SocketTimeoutException, SocketException {
@ -849,13 +850,7 @@ public class WolfSSLSession {
* occur if needed */
ret = write(localPtr, data, offset, length, timeout);
if (ret == WOLFJNI_TIMEOUT) {
throw new SocketTimeoutException("Socket write timeout");
}
else if (ret == WOLFJNI_SELECT_FAIL) {
throw new SocketException("Socket select() failed, errno = " +
WolfSSL.getErrno());
}
throwExceptionFromIOReturnValue(ret, "wolfSSL_write()");
return ret;
}
@ -893,10 +888,12 @@ public class WolfSSLSession {
* get a specific error code.
* <code>BAD_FUNC_ARC</code> when bad arguments are used.
* @throws IllegalStateException WolfSSLContext has been freed
* @throws SocketException Native socket select() failed
* @throws SocketTimeoutException if socket timeout occurs, should not
* occur since infinite timeout is used for this call.
* @throws SocketException Native socket select/poll() failed
*/
public int read(byte[] data, int sz)
throws IllegalStateException, SocketException {
throws IllegalStateException, SocketTimeoutException, SocketException {
int ret;
long localPtr;
@ -916,10 +913,7 @@ public class WolfSSLSession {
* occur if needed */
ret = read(localPtr, data, 0, sz, 0);
if (ret == WOLFJNI_SELECT_FAIL) {
throw new SocketException("Socket select() failed, errno = " +
WolfSSL.getErrno());
}
throwExceptionFromIOReturnValue(ret, "wolfSSL_read()");
return ret;
}
@ -959,7 +953,7 @@ public class WolfSSLSession {
* <code>BAD_FUNC_ARC</code> when bad arguments are used.
* @throws IllegalStateException WolfSSLContext has been freed
* @throws SocketTimeoutException if socket timeout occurs
* @throws SocketException Native socket select() failed
* @throws SocketException Native socket select/poll() failed
*/
public int read(byte[] data, int sz, int timeout)
throws IllegalStateException, SocketTimeoutException, SocketException {
@ -1005,7 +999,7 @@ public class WolfSSLSession {
* <code>BAD_FUNC_ARC</code> when bad arguments are used.
* @throws IllegalStateException WolfSSLContext has been freed
* @throws SocketTimeoutException if socket timeout occurs
* @throws SocketException Native socket select() failed
* @throws SocketException Native socket select/poll() failed
*/
public int read(byte[] data, int offset, int sz, int timeout)
throws IllegalStateException, SocketTimeoutException, SocketException {
@ -1028,13 +1022,7 @@ public class WolfSSLSession {
* occur if needed */
ret = read(localPtr, data, offset, sz, timeout);
if (ret == WOLFJNI_TIMEOUT) {
throw new SocketTimeoutException("Socket read timeout");
}
else if (ret == WOLFJNI_SELECT_FAIL) {
throw new SocketException("Socket select() failed, errno = " +
WolfSSL.getErrno());
}
throwExceptionFromIOReturnValue(ret, "wolfSSL_read()");
return ret;
}
@ -1063,31 +1051,14 @@ public class WolfSSLSession {
* error code, call <code>getError()</code>.
* @throws IllegalStateException WolfSSLContext has been freed
* @throws SocketTimeoutException if underlying socket timed out
* @throws SocketException Native socket select() failed
* @throws SocketException Native socket select/accept() failed
* @see #getError(int)
* @see #connect()
*/
public int accept()
throws IllegalStateException, SocketTimeoutException, SocketException {
int ret;
confirmObjectIsActive();
synchronized (sslLock) {
ret = accept(this.sslPtr, 0);
}
if (ret == WolfSSL.WOLFJNI_TIMEOUT) {
throw new SocketTimeoutException(
"Native socket timed out during SSL_accept()");
}
else if (ret == WOLFJNI_SELECT_FAIL) {
throw new SocketException("Socket select() failed, errno = " +
WolfSSL.getErrno());
}
return ret;
return accept(0);
}
/**
@ -1132,14 +1103,7 @@ public class WolfSSLSession {
ret = accept(this.sslPtr, timeout);
}
if (ret == WolfSSL.WOLFJNI_TIMEOUT) {
throw new SocketTimeoutException(
"Native socket timed out during SSL_accept()");
}
else if (ret == WOLFJNI_SELECT_FAIL) {
throw new SocketException("Socket select() failed, errno = " +
WolfSSL.getErrno());
}
throwExceptionFromIOReturnValue(ret, "wolfSSL_accept()");
return ret;
}
@ -1199,28 +1163,17 @@ public class WolfSSLSession {
* <code>SSL_FATAL_ERROR</code> upon failure. Call <code>
* getError()</code> for a more specific error code.
* @throws IllegalStateException WolfSSLContext has been freed
* @throws SocketException Native socket select() failed
* @throws SocketTimeoutException if socket timeout occurs, should not
* since infinite timeout is used for this call.
* @throws SocketException Native socket select/poll() failed
* @see #shutdownSSL(int)
* @see #freeSSL(long)
* @see WolfSSLContext#free()
*/
public int shutdownSSL()
throws IllegalStateException, SocketException {
throws IllegalStateException, SocketTimeoutException, SocketException {
int ret;
confirmObjectIsActive();
synchronized (sslLock) {
ret = shutdownSSL(this.sslPtr, 0);
}
if (ret == WOLFJNI_SELECT_FAIL) {
throw new SocketException("Socket select() failed, errno = " +
WolfSSL.getErrno());
}
return ret;
return shutdownSSL(0);
}
/**
@ -1250,8 +1203,8 @@ public class WolfSSLSession {
* <code>SSL_FATAL_ERROR</code> upon failure. Call <code>
* getError()</code> for a more specific error code.
* @throws IllegalStateException WolfSSLContext has been freed
* @throws SocketTimeoutException if read timeout occurs.
* @throws SocketException Native socket select() failed
* @throws SocketTimeoutException if socket timeout occurs.
* @throws SocketException Native socket select/poll() failed
* @see #freeSSL(long)
* @see WolfSSLContext#free()
*/
@ -1266,13 +1219,7 @@ public class WolfSSLSession {
ret = shutdownSSL(this.sslPtr, timeout);
}
if (ret == WOLFJNI_TIMEOUT) {
throw new SocketTimeoutException("Socket read timeout");
}
else if (ret == WOLFJNI_SELECT_FAIL) {
throw new SocketException("Socket select() failed, errno = " +
WolfSSL.getErrno());
}
throwExceptionFromIOReturnValue(ret, "wolfSSL_shutdown()");
return ret;
}

View File

@ -353,8 +353,11 @@ public class WolfSSLEngine extends SSLEngine {
*
* @return WolfSSL.SSL_SUCCESS on success, zero or negative on error
* @throws SocketException if ssl.shutdownSSL() encounters a socket error
* @throws SocketTimeoutException if ssl.shutdownSSL() times out
*/
private synchronized int ClosingConnection() throws SocketException {
private synchronized int ClosingConnection()
throws SocketException, SocketTimeoutException {
int ret;
/* Save session into WolfSSLAuthStore cache, saves session
@ -395,7 +398,13 @@ public class WolfSSLEngine extends SSLEngine {
/**
* Starts or continues SSL/TLS handshake.
* Returns WolfSSL.SSL_SUCCESS or WolfSSL.SSL_FAILURE
*
* @return WolfSSL.SSL_SUCCESS or WolfSSL.SSL_FAILURE
* @throws SocketException if ssl.connect() or ssl.accept() encounters
* a socket exception.
* @throws SocketTimeoutException if ssl.connect() or ssl.accept()
* times out. This should not happen since infinite timeout is
* being used for these calls.
*/
private synchronized int DoHandshake() throws SSLException {
int ret = WolfSSL.SSL_SUCCESS;
@ -434,10 +443,12 @@ public class WolfSSLEngine extends SSLEngine {
* (SSLSession.getApplicationBufferSize()).
*
* @throws SocketException if ssl.write() encounters a socket error
* @throws SocketTimeoutException if ssl.write() times out. Shouldn't
* happen since this is using an infinite timeout.
* @return bytes sent on success, negative on error
*/
private synchronized int SendAppData(ByteBuffer[] in, int ofst, int len)
throws SocketException {
throws SocketException, SocketTimeoutException {
int i = 0;
int ret = 0;
@ -629,7 +640,7 @@ public class WolfSSLEngine extends SSLEngine {
try {
ClosingConnection();
} catch (SocketException e) {
} catch (SocketException | SocketTimeoutException e) {
throw new SSLException(e);
}
produced += CopyOutPacket(out);
@ -653,7 +664,7 @@ public class WolfSSLEngine extends SSLEngine {
if (ret > 0) {
consumed += ret;
}
} catch (SocketException e) {
} catch (SocketException | SocketTimeoutException e) {
throw new SSLException(e);
}
}
@ -781,7 +792,7 @@ public class WolfSSLEngine extends SSLEngine {
synchronized (ioLock) {
try {
ret = this.ssl.read(tmp, maxOutSz);
} catch (SocketException e) {
} catch (SocketTimeoutException | SocketException e) {
throw new SSLException(e);
}
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
@ -821,7 +832,8 @@ public class WolfSSLEngine extends SSLEngine {
* 0, or err */
ret = 0;
}
} catch (SocketException e) {
} catch (SocketException |
SocketTimeoutException e) {
throw new SSLException(e);
}
return ret;
@ -998,7 +1010,7 @@ public class WolfSSLEngine extends SSLEngine {
* release, global JNI verify callback pointer */
this.engineHelper.unsetVerifyCallback();
}
} catch (SocketException e) {
} catch (SocketException | SocketTimeoutException e) {
throw new SSLException(e);
}
}

View File

@ -1220,7 +1220,7 @@ public class WolfSSLEngineHelper {
/* TODO: SunJSSE sends a Handshake Failure alert instead here */
try {
this.ssl.shutdownSSL();
} catch (SocketException e) {
} catch (SocketException | SocketTimeoutException e) {
throw new SSLException(e);
}
@ -1336,7 +1336,8 @@ public class WolfSSLEngineHelper {
} catch (SocketException e) {
/* SocketException may be thrown if native socket
* select() fails. Propogate errno back inside exception. */
* select/poll() fails. Propogate errno back inside new
* SSLException. */
throw new SSLException(e);
}

View File

@ -943,7 +943,7 @@ public class WolfSSLSocket extends SSLSocket {
"Failed to set native Socket fd");
}
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"registered SSLSocket with native wolfSSL");
"registered SSLSocket(this) with native wolfSSL");
} else {
ret = ssl.setFd(this.socket);
@ -962,7 +962,7 @@ public class WolfSSLSocket extends SSLSocket {
}
else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"registered Socket with native wolfSSL");
"registered Socket(this.socket) with native wolfSSL");
}
}
}
@ -1888,14 +1888,23 @@ public class WolfSSLSocket extends SSLSocket {
}
}
if (this.socket != null) {
ret = ssl.shutdownSSL(this.socket.getSoTimeout());
} else {
ret = ssl.shutdownSSL(super.getSoTimeout());
}
try {
if (this.socket != null) {
ret = ssl.shutdownSSL(
this.socket.getSoTimeout());
} else {
ret = ssl.shutdownSSL(
super.getSoTimeout());
}
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"ssl.shutdownSSL() ret = " + ret);
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"ssl.shutdownSSL() ret = " + ret);
} catch (SocketException | SocketTimeoutException e) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"Exception while trying to ssl.shutdownSSL(), " +
"ignoring to finish cleanup");
}
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"thread trying to get handshakeLock");
@ -2052,7 +2061,8 @@ public class WolfSSLSocket extends SSLSocket {
InetSocketAddress address = null;
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"entered connect(SocketAddress endpoint, int timeout)");
"entered connect(SocketAddress endpoint, int timeout / " +
timeout + " ms)");
if (!(endpoint instanceof InetSocketAddress)) {
throw new IllegalArgumentException("endpoint is not of type " +
@ -2066,6 +2076,8 @@ public class WolfSSLSocket extends SSLSocket {
}
address = (InetSocketAddress)endpoint;
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"Underlying Java Socket connected to peer: " + address);
/* register host/port for session resumption in case where
createSocket() was called without host/port, but
@ -2510,6 +2522,16 @@ public class WolfSSLSocket extends SSLSocket {
" (error code: " + err + ")");
}
} catch (SocketException e) {
/* ssl.read() can throw SocketException from poll() if fd
* closed or peer shut down connection */
if (e.getMessage().contains("fd closed during poll") ||
e.getMessage().contains("disconnected during poll")) {
/* end of stream */
return -1;
}
throw e;
} catch (IllegalStateException e) {
throw new IOException(e);
}