diff --git a/README.md b/README.md
index d66a818b..37033a94 100644
--- a/README.md
+++ b/README.md
@@ -525,7 +525,9 @@ wolfSSL JNI/JSSE does not select/poll on a large number of file descriptors
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.
+used as the default descriptor monitoring function. This limit does not apply
+to Windows, where `FD_SETSIZE` is the number of sockets an `fd_set` can hold
+rather than a limit on the socket value.
### Security Property Support
diff --git a/native/com_wolfssl_WolfSSL.c b/native/com_wolfssl_WolfSSL.c
index 235038b1..2fdc7810 100644
--- a/native/com_wolfssl_WolfSSL.c
+++ b/native/com_wolfssl_WolfSSL.c
@@ -3062,7 +3062,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_getErrno
#ifndef USE_WINDOWS_API
return errno;
#else
- return 0;
+ return WSAGetLastError();
#endif
}
diff --git a/native/com_wolfssl_WolfSSLSession.c b/native/com_wolfssl_WolfSSLSession.c
index 1b7ca84e..fbbb2874 100644
--- a/native/com_wolfssl_WolfSSLSession.c
+++ b/native/com_wolfssl_WolfSSLSession.c
@@ -88,12 +88,18 @@ static jobject g_crlCbIfaceObj;
*
* interruptFds[2] is a pipe() used for non-Windows platforms. This pipe is
* used to interrupt threads blocked inside select()/poll() when a separate
- * Java thread calls close() on the SSLSocket. */
+ * Java thread calls close() on the SSLSocket. Windows has no pipe() usable
+ * with select(). There, SSLSocket.close() closing the SOCKET wakes threads
+ * blocked in select(), which socketSelect() reports as
+ * WOLFJNI_IO_EVENT_FD_CLOSED, and ioInterrupted makes later socketSelect()
+ * calls return without waiting. */
typedef struct SSLAppData {
int threadsInPoll; /* number of threads in poll/select() */
#ifndef USE_WINDOWS_API
int interruptFds[2]; /* pipe for interrupting socketSelect() */
wolfSSL_Mutex* pollCountLock; /* lock around threadsInPoll */
+#else
+ volatile int ioInterrupted; /* set by interruptBlockedIO() */
#endif
wolfSSL_Mutex* jniSessLock; /* WOLFSSL session lock */
jobject* g_verifySSLCbIfaceObj; /* Java verify callback [global ref] */
@@ -518,7 +524,8 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_setFd(JNIEnv* jenv,
jobject impl;
jobject fdesc;
#ifdef USE_WINDOWS_API
- unsigned long blocking = 0;
+ /* FIONBIO enables non-blocking mode when set to non-zero */
+ unsigned long nonBlocking = 1;
#endif
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
(void)jcl;
@@ -629,12 +636,17 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_setFd(JNIEnv* jenv,
/* set socket to non-blocking so we can use select() to detect
* WANT_READ / WANT_WRITE */
#ifdef USE_WINDOWS_API
- ret = ioctlsocket(fd, FIONBIO, &blocking);
- if (ret == SOCKET_ERROR) {
- ret = SSL_FAILURE;
- }
- else {
- ret = SSL_SUCCESS;
+ /* Only for Socket (type 1). DatagramSocket stays blocking, since wolfSSL
+ * is not told the socket is non-blocking and relies on a blocking
+ * socket receive timeout to retransmit DTLS handshake messages. */
+ if (type == 1) {
+ ret = ioctlsocket(fd, FIONBIO, &nonBlocking);
+ if (ret == SOCKET_ERROR) {
+ ret = SSL_FAILURE;
+ }
+ else {
+ ret = SSL_SUCCESS;
+ }
}
#else
ret = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
@@ -841,8 +853,10 @@ enum {
* ready for read/write, or timeout. Note that we explicitly set the underlying
* socket descriptor to non-blocking.
*
- * 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).
+ * NOTE: on POSIX 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 Windows FD_SETSIZE is the number of SOCKET slots in an
+ * fd_set (64 by default), not a limit on the SOCKET value.
*
* 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.
@@ -891,11 +905,27 @@ static int socketSelect(SSLAppData* appData, int sockfd, int timeout_ms, int rx,
return WOLFJNI_IO_EVENT_ERROR;
}
+#ifdef USE_WINDOWS_API
+ /* Winsock FD_SET stores the SOCKET in the next free slot of fd_array
+ * and ignores it once all FD_SETSIZE slots are used, so SOCKET values
+ * above FD_SETSIZE are valid. Only one SOCKET is placed in each set
+ * here, reject only invalid descriptors. */
+ if (sockfd < 0) {
+ return WOLFJNI_IO_EVENT_ERROR;
+ }
+
+ /* Once SSLSocket.close() has started, return instead of waiting, as the
+ * interrupt pipe does on other platforms. shutdownSSL() still waits. */
+ if ((shutdown == 0) && (appData->ioInterrupted != 0)) {
+ return WOLFJNI_IO_EVENT_FD_CLOSED;
+ }
+#else
/* Guard against FD_SET overflow. FD_SET behavior is undefined
- * if sockfd >= FD_SETSIZE (1024 on Linux, 64 on Windows). */
+ * if sockfd >= FD_SETSIZE (1024 by default). */
if (sockfd < 0 || sockfd >= FD_SETSIZE) {
return WOLFJNI_IO_EVENT_ERROR;
}
+#endif
#ifndef USE_WINDOWS_API
do {
@@ -987,6 +1017,13 @@ static int socketSelect(SSLAppData* appData, int sockfd, int timeout_ms, int rx,
if ((result == -1) && (errno == EBADF)) {
return WOLFJNI_IO_EVENT_FD_CLOSED;
}
+#else
+ /* Winsock select() fails with WSAENOTSOCK if the SOCKET is not valid,
+ * expected cause is the socket being closed concurrently. Report as a
+ * closed fd, matching POSIX EBADF case above. */
+ if ((result == SOCKET_ERROR) && (WSAGetLastError() == WSAENOTSOCK)) {
+ return WOLFJNI_IO_EVENT_FD_CLOSED;
+ }
#endif
return WOLFJNI_IO_EVENT_FAIL;
@@ -1273,6 +1310,16 @@ static int SSLWriteNonblockingWithSelectPoll(WOLFSSL* ssl, byte* data,
break;
}
+ #ifdef USE_WINDOWS_API
+ /* Once close() has started, restoreFdBlocking() may have made the
+ * SOCKET blocking, where send() would wait without a timeout */
+ if (appData->ioInterrupted != 0) {
+ wc_UnLockMutex(jniSessLock);
+ ret = WOLFJNI_IO_EVENT_FD_CLOSED;
+ break;
+ }
+ #endif
+
ret = wolfSSL_write(ssl, data, length);
err = wolfSSL_get_error(ssl, ret);
@@ -1510,6 +1557,16 @@ static int SSLReadNonblockingWithSelectPoll(WOLFSSL* ssl, byte* out,
break;
}
+ #ifdef USE_WINDOWS_API
+ /* Once close() has started, restoreFdBlocking() may have made the
+ * SOCKET blocking, where recv() would wait without a timeout */
+ if (appData->ioInterrupted != 0) {
+ wc_UnLockMutex(jniSessLock);
+ size = WOLFJNI_IO_EVENT_FD_CLOSED;
+ break;
+ }
+ #endif
+
size = wolfSSL_read(ssl, out, length);
err = wolfSSL_get_error(ssl, size);
@@ -6902,26 +6959,29 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_useClientSuites
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_interruptBlockedIO
(JNIEnv* jenv, jobject jcl, jlong sslPtr)
{
-#ifndef USE_WINDOWS_API
SSLAppData* appData = NULL;
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
-#endif
(void)jenv;
(void)jcl;
-#ifndef USE_WINDOWS_API
/* get session mutex from SSL app data */
appData = (SSLAppData*)wolfSSL_get_app_data(ssl);
if (appData == NULL) {
return WOLFSSL_FAILURE;
}
+#ifndef USE_WINDOWS_API
/* signal any blocked threads in select()/poll() to wake up, so we
* don't have a deadlock when trying to lock jniSessLock next */
writeToInterruptPipe(appData);
writeToInterruptPipe(appData);
-
-#endif /* USE_WINDOWS_API */
+#else
+ /* No interrupt pipe on Windows. Make later socketSelect() calls return
+ * instead of waiting, for example the session ticket wait in
+ * get1Session() during close(). Threads already in select() wake when
+ * the SOCKET is closed. */
+ appData->ioInterrupted = 1;
+#endif
return SSL_SUCCESS;
}
@@ -6944,6 +7004,49 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_getThreadsBlockedInPoll
return (jint)threadsWaitingInPollSelect(appData);
}
+JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_restoreFdBlocking
+ (JNIEnv* jenv, jobject jcl, jlong sslPtr)
+{
+#ifdef USE_WINDOWS_API
+ int fd;
+ int ret = SSL_SUCCESS;
+ unsigned long nonBlocking = 0;
+ SSLAppData* appData = NULL;
+ WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
+#endif
+ (void)jenv;
+ (void)jcl;
+
+#ifdef USE_WINDOWS_API
+ appData = (SSLAppData*)wolfSSL_get_app_data(ssl);
+ if ((appData == NULL) || (appData->jniSessLock == NULL)) {
+ return SSL_FAILURE;
+ }
+
+ /* Undo FIONBIO set in setFd(). Winsock closesocket() on a non-blocking
+ * SOCKET with SO_LINGER enabled fails with WSAEWOULDBLOCK and leaves the
+ * SOCKET open. Hold jniSessLock so the mode does not change during
+ * another thread's wolfSSL_read()/wolfSSL_write(). Those check
+ * ioInterrupted under the same lock and stop before using the SOCKET
+ * again, so threads still in select() don't resume on a blocking SOCKET. */
+ if (wc_LockMutex(appData->jniSessLock) != 0) {
+ return SSL_FAILURE;
+ }
+ fd = wolfSSL_get_fd(ssl);
+ if ((fd >= 0) &&
+ (ioctlsocket(fd, FIONBIO, &nonBlocking) == SOCKET_ERROR)) {
+ ret = SSL_FAILURE;
+ }
+ wc_UnLockMutex(appData->jniSessLock);
+
+ return ret;
+#else
+ (void)sslPtr;
+
+ return SSL_SUCCESS;
+#endif /* USE_WINDOWS_API */
+}
+
JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLSession_setSSLIORecv
(JNIEnv* jenv, jobject jcl, jlong sslPtr)
{
diff --git a/native/com_wolfssl_WolfSSLSession.h b/native/com_wolfssl_WolfSSLSession.h
index 2e8cd919..9085d7f8 100644
--- a/native/com_wolfssl_WolfSSLSession.h
+++ b/native/com_wolfssl_WolfSSLSession.h
@@ -1127,6 +1127,14 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_interruptBlockedIO
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_getThreadsBlockedInPoll
(JNIEnv *, jobject, jlong);
+/*
+ * Class: com_wolfssl_WolfSSLSession
+ * Method: restoreFdBlocking
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_restoreFdBlocking
+ (JNIEnv *, jobject, jlong);
+
/*
* Class: com_wolfssl_WolfSSLSession
* Method: setMTU
diff --git a/src/java/com/wolfssl/WolfSSL.java b/src/java/com/wolfssl/WolfSSL.java
index 0ccc8156..d53aab7b 100644
--- a/src/java/com/wolfssl/WolfSSL.java
+++ b/src/java/com/wolfssl/WolfSSL.java
@@ -85,13 +85,14 @@ public enum TLS_VERSION {
public static final int WOLFJNI_IO_EVENT_TIMEOUT = -11;
/**
- * Socket poll() exceptional error, matches com_wolfssl_WolfSSLSession.c
- * socketPoll() return value */
+ * Socket select/poll() exceptional error, matches
+ * com_wolfssl_WolfSSLSession.c socketSelect()/socketPoll() return value.
+ */
public static final int WOLFJNI_IO_EVENT_ERROR = -14;
/**
* Socket file descriptor closed, matches com_wolfssl_WolfSSLSession.c
- * socketPoll() return value */
+ * socketSelect() and socketPoll() return value */
public static final int WOLFJNI_IO_EVENT_FD_CLOSED = -15;
/**
@@ -738,6 +739,10 @@ public WolfSSL() throws WolfSSLException {
"Failed to initialize wolfSSL library: " + ret);
}
+ /* Resolve getErrno() early. On Windows the JVM first-call symbol
+ * lookup would overwrite WSAGetLastError() before it is read. */
+ getErrno();
+
/* Populate NID values from native wolfSSL enums */
NID_surname = getNID_surname();
NID_serialNumber = getNID_serialNumber();
@@ -2076,9 +2081,9 @@ public static int cryptoCbUnRegisterDevice(int devId) {
* error is encountered. This JNI method is a simple getter to retrive
* the current system errno value.
*
- * If on Windows, this will return 0 (no errno support on Windows)
+ * On Windows, this returns the value of WSAGetLastError().
*
- * @return System native errno value
+ * @return System native errno value, or WSAGetLastError() on Windows
*/
public static native int getErrno();
diff --git a/src/java/com/wolfssl/WolfSSLSession.java b/src/java/com/wolfssl/WolfSSLSession.java
index e35a7d62..ee4d5fd3 100644
--- a/src/java/com/wolfssl/WolfSSLSession.java
+++ b/src/java/com/wolfssl/WolfSSLSession.java
@@ -760,6 +760,7 @@ private native int useALPNByteArray(long ssl, byte[] protocols,
private native int useClientSuites(long ssl);
private native int interruptBlockedIO(long ssl);
private native int getThreadsBlockedInPoll(long ssl);
+ private native int restoreFdBlocking(long ssl);
private native int setMTU(long ssl, int mtu);
private native String stateStringLong(long ssl);
private native int getMaxOutputSize(long ssl);
@@ -1041,12 +1042,12 @@ private static void throwExceptionFromIOReturnValue(
"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());
+ throw new SocketException("Socket fd closed during " +
+ "select/poll(), errno = " + WolfSSL.getErrno());
}
else if (ret == WolfSSL.WOLFJNI_IO_EVENT_ERROR) {
- throw new SocketException("Socket fd poll() exceptional error, " +
- "errno = " + WolfSSL.getErrno());
+ throw new SocketException("Socket fd select/poll() exceptional " +
+ "error, errno = " + WolfSSL.getErrno());
}
else if (ret == WolfSSL.WOLFJNI_IO_EVENT_POLLHUP) {
throw new SocketException("Socket disconnected during poll(), " +
@@ -5727,6 +5728,12 @@ public void setIOSendByteBuffer(WolfSSLByteBufferIOSendCallback callback)
*
* This is used by wolfJSSE when SSLSocket.close() is called, to wake up
* threads that are blocked in select()/poll().
+ *
+ * On Windows, threads already blocked in select() are not woken by this
+ * call, they wake when the socket is closed. After this call, read()
+ * and write() on this session throw SocketException without using the
+ * socket, and other select() waits return without waiting, except in
+ * shutdownSSL().
*
* @return WolfSSL.SSL_SUCCESS on success, negative on error.
*
@@ -5767,6 +5774,36 @@ public synchronized int getThreadsBlockedInPoll()
return getThreadsBlockedInPoll(this.sslPtr);
}
+ /**
+ * Put the socket set with setFd() back into blocking mode.
+ *
+ * setFd() puts the socket into non-blocking mode. On Windows,
+ * closesocket() on a non-blocking socket with SO_LINGER enabled fails
+ * and leaves the socket open. Call this after shutdownSSL() and before
+ * the socket is closed. Does nothing on other platforms.
+ *
+ * @return WolfSSL.SSL_SUCCESS on success, WolfSSL.SSL_FAILURE if the
+ * socket mode could not be changed.
+ *
+ * @throws IllegalStateException WolfSSLSession has been freed
+ */
+ public int restoreFdBlocking() throws IllegalStateException {
+
+ final int ret;
+
+ confirmObjectIsActive();
+
+ synchronized (sslLock) {
+ WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
+ WolfSSLDebug.INFO, this.sslPtr,
+ () -> "entered restoreFdBlocking()");
+
+ ret = restoreFdBlocking(this.sslPtr);
+ }
+
+ return ret;
+ }
+
/**
* Use SNI name with this session.
*
diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java b/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java
index c83f808a..684de618 100644
--- a/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java
+++ b/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java
@@ -2228,6 +2228,10 @@ private boolean ioStreamsAreClosed() {
* If this socket was created with an autoClose value set to true,
* this will also close the underlying Socket.
*
+ * If SO_LINGER is enabled with a non-zero timeout, this waits up to that
+ * many seconds for the TLS close_notify alert to be sent. Otherwise
+ * close_notify is sent without waiting.
+ *
* A native session free that fails is reported as IOException once this
* socket is otherwise closed and its transport released. One deferred to
* an I/O thread still running here cannot report back to this caller and
@@ -2316,16 +2320,19 @@ public synchronized void close() throws IOException {
}
try {
- /* Use SO_LINGER value when calling
- * shutdown here, since we are closing the
- * socket */
+ /* Wait up to SO_LINGER seconds for close_notify
+ * to be sent, since we are closing the socket.
+ * shutdownSSL() takes milliseconds and treats 0
+ * as no limit, so pass -1 to not wait when
+ * SO_LINGER is disabled (-1) or 0. */
+ final int linger;
if (this.socket != null) {
- ret = ssl.shutdownSSL(
- this.socket.getSoLinger());
+ linger = this.socket.getSoLinger();
} else {
- ret = ssl.shutdownSSL(
- super.getSoLinger());
+ linger = super.getSoLinger();
}
+ ret = ssl.shutdownSSL(
+ (linger > 0) ? (linger * 1000) : -1);
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "ssl.shutdownSSL() ret = " + ret);
@@ -2396,6 +2403,22 @@ public synchronized void close() throws IOException {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "thread got ioLock (freeSSL)");
+ /* setFd() made the socket non-blocking. Restore blocking
+ * mode before the socket is closed, here or later by the
+ * application, so a Windows close with SO_LINGER does
+ * not fail and leave the socket open. Skip this once the
+ * transport is closed, since its descriptor value may
+ * already belong to another socket. */
+ if ((this.ssl != null) && !this.connectionClosed &&
+ !((this.socket != null) ? this.socket.isClosed() :
+ super.isClosed())) {
+ try {
+ this.ssl.restoreFdBlocking();
+ } catch (IllegalStateException e) {
+ /* session already freed, nothing to restore */
+ }
+ }
+
/* Free the native WOLFSSL session now to release memory
* and the interruptFds[] pipe earlier than finalize(),
* or defer to the last exiting I/O operation if a thread
@@ -3008,12 +3031,12 @@ public synchronized int read(byte[] b, int off, int len)
}
} catch (SocketException e) {
- /* ssl.read() can throw SocketException from poll() if fd
- * closed or peer shut down connection */
+ /* ssl.read() can throw SocketException from select/poll()
+ * if fd closed or peer shut down connection. */
String msg = e.getMessage();
if (msg != null &&
- (msg.contains("fd closed during poll") ||
- msg.contains("disconnected during poll"))) {
+ (msg.contains("fd closed during") ||
+ msg.contains("disconnected during"))) {
/* end of stream */
return -1;
}
@@ -3207,6 +3230,13 @@ public synchronized void write(byte[] b, int off, int len)
}
if (ret < 0) {
+ /* A blocked write() fails when close() from another
+ * thread closes the transport. Report that as a
+ * closed socket, matching the check above. */
+ if (this.isClosed || socket.isClosed()) {
+ throw new SocketException("Socket is closed");
+ }
+
/* print error description string */
String errStr = WolfSSL.getErrorString(err);
throw new IOException(
diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLContextTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLContextTest.java
index dc328089..cf89b59c 100644
--- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLContextTest.java
+++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLContextTest.java
@@ -104,6 +104,10 @@ public static void testProviderInstallationAtRuntime()
e.printStackTrace();
}
+ /* Start static lists empty so running this again in the same JVM does
+ * not add duplicate entries */
+ enabledProtocols.clear();
+
/* populate enabledProtocols */
synchronized (WolfSSLTestFactory.jdkTlsDisabledAlgorithmsLock) {
for (int i = 0; i < allProtocols.length; i++) {
diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java
index 32683ad5..ba77a735 100644
--- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java
+++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLEngineTest.java
@@ -136,6 +136,10 @@ public static void testProviderInstallationAtRuntime()
Provider p = Security.getProvider("wolfJSSE");
assertNotNull(p);
+ /* Start static lists empty so running this again in the same JVM does
+ * not add duplicate entries */
+ enabledProtocols.clear();
+
/* populate enabledProtocols */
for (int i = 0; i < allProtocols.length; i++) {
try {
@@ -1725,8 +1729,7 @@ public void closeSocket() {
public void run() {
try {
SSLContext ctx = tf.createSSLContext("TLS", engineProvider);
- ss = (SSLServerSocket)ctx
- .getServerSocketFactory().createServerSocket(serverPort);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, serverPort);
while (clientConnections > 0) {
serverOpenLatch.countDown();
diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLParametersPskTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLParametersPskTest.java
index fca50e2d..43c9e98e 100644
--- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLParametersPskTest.java
+++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLParametersPskTest.java
@@ -561,8 +561,7 @@ public void testPskSocketHandshake() throws Exception {
SSLContext ctx = SSLContext.getInstance("TLSv1.2", engineProvider);
ctx.init(null, null, null);
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
int port = ss.getLocalPort();
final CountDownLatch latch = new CountDownLatch(1);
@@ -638,8 +637,7 @@ public void testPskSocketKeepArrays() throws Exception {
SSLContext ctx = SSLContext.getInstance("TLSv1.2", engineProvider);
ctx.init(null, null, null);
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
int port = ss.getLocalPort();
final CountDownLatch latch = new CountDownLatch(1);
diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLServerSocketFactoryTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLServerSocketFactoryTest.java
index f2f6edb1..5fabed16 100644
--- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLServerSocketFactoryTest.java
+++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLServerSocketFactoryTest.java
@@ -95,6 +95,11 @@ public static void testSetupSocketFactory() throws NoSuchProviderException,
Provider p = Security.getProvider("wolfJSSE");
assertNotNull(p);
+ /* Static lists are filled below. Start empty so running this again
+ * in the same JVM does not add duplicate entries */
+ enabledProtocols.clear();
+ sockFactories.clear();
+
/* populate enabledProtocols */
for (int i = 0; i < allProtocols.length; i++) {
try {
diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLServerSocketTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLServerSocketTest.java
index 8db92e48..0f6da343 100644
--- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLServerSocketTest.java
+++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLServerSocketTest.java
@@ -113,6 +113,11 @@ public static void testSetupSocketFactory() throws NoSuchProviderException,
Provider p = Security.getProvider("wolfJSSE");
assertNotNull(p);
+ /* Static lists are filled below. Start empty so running this again
+ * in the same JVM does not add duplicate entries */
+ enabledProtocols.clear();
+ sockFactories.clear();
+
/* populate enabledProtocols */
for (int i = 0; i < allProtocols.length; i++) {
try {
@@ -169,7 +174,7 @@ public static void testSetupSocketFactory() throws NoSuchProviderException,
* configuration of 'jdk.tls.disabledAlgorithms' can turn off
* protocols such as 'TLS 1.1' leaving a factory with none
* available */
- SSLServerSocket s = (SSLServerSocket)sf.createServerSocket(0);
+ SSLServerSocket s = WolfSSLTestFactory.createServerSocket(sf, 0);
if (s.getEnabledProtocols().length > 0) {
sockFactories.add(sf);
}
@@ -180,8 +185,7 @@ public static void testSetupSocketFactory() throws NoSuchProviderException,
public void testGetSSLParameters() throws Exception {
/* create new context, SSLServerSocket */
this.ctx = tf.createSSLContext("TLS", "wolfJSSE");
- SSLServerSocket ss = (SSLServerSocket)
- ctx.getServerSocketFactory().createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
SSLParameters p = ss.getSSLParameters();
assertNotNull(p);
@@ -261,7 +265,7 @@ public void testGetSupportedCipherSuites()
for (int i = 0; i < sockFactories.size(); i++) {
SSLServerSocketFactory sf = sockFactories.get(i);
- SSLServerSocket s = (SSLServerSocket)sf.createServerSocket(0);
+ SSLServerSocket s = WolfSSLTestFactory.createServerSocket(sf, 0);
String[] cipherSuites = s.getSupportedCipherSuites();
s.close();
@@ -278,7 +282,7 @@ public void testGetEnabledCipherSuites()
for (int i = 0; i < sockFactories.size(); i++) {
SSLServerSocketFactory sf = sockFactories.get(i);
- SSLServerSocket s = (SSLServerSocket)sf.createServerSocket(0);
+ SSLServerSocket s = WolfSSLTestFactory.createServerSocket(sf, 0);
String[] cipherSuites = s.getEnabledCipherSuites();
s.close();
@@ -296,7 +300,7 @@ public void testGetSetEnabledCipherSuites()
for (int i = 0; i < sockFactories.size(); i++) {
SSLServerSocketFactory sf = sockFactories.get(i);
- SSLServerSocket s = (SSLServerSocket)sf.createServerSocket(0);
+ SSLServerSocket s = WolfSSLTestFactory.createServerSocket(sf, 0);
String[] cipherSuites = s.getEnabledCipherSuites();
if (cipherSuites == null) {
@@ -357,7 +361,7 @@ public void testGetSupportedProtocols()
for (int i = 0; i < sockFactories.size(); i++) {
SSLServerSocketFactory sf = sockFactories.get(i);
- SSLServerSocket s = (SSLServerSocket)sf.createServerSocket(0);
+ SSLServerSocket s = WolfSSLTestFactory.createServerSocket(sf, 0);
String[] protocols = s.getSupportedProtocols();
if (protocols == null) {
@@ -376,7 +380,7 @@ public void testGetSetEnabledProtocols()
for (int i = 0; i < sockFactories.size(); i++) {
SSLServerSocketFactory sf = sockFactories.get(i);
- SSLServerSocket s = (SSLServerSocket)sf.createServerSocket(0);
+ SSLServerSocket s = WolfSSLTestFactory.createServerSocket(sf, 0);
String[] protocols = s.getEnabledProtocols();
if (protocols == null) {
@@ -440,8 +444,7 @@ public void testEnableSessionCreation() throws Exception {
this.ctx = tf.createSSLContext("TLS", ctxProvider);
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
@@ -503,8 +506,7 @@ public void testSetNeedClientAuth() throws Exception {
this.ctx = tf.createSSLContext("TLS", ctxProvider);
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
ss.setWantClientAuth(true);
assertTrue(ss.getWantClientAuth());
@@ -551,8 +553,7 @@ public Void call() throws Exception {
tf.createTrustManager("SunX509", tf.caServerJKS, ctxProvider),
tf.createKeyManager("SunX509", tf.clientJKS, ctxProvider));
- ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
ss.setWantClientAuth(true);
ss.setNeedClientAuth(true);
@@ -610,8 +611,7 @@ pass with setNeedClientAuth(false) */
tf.createTrustManager("SunX509", tf.caServerJKS, ctxProvider),
tf.createKeyManager("SunX509", tf.clientJKS, ctxProvider));
- ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
- .createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(srvCtx, 0);
ss.setWantClientAuth(false);
ss.setNeedClientAuth(false);
@@ -657,8 +657,7 @@ public void testSetUseClientMode() throws Exception {
this.ctx = tf.createSSLContext("TLS", "wolfJSSE");
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
/* test getter/setter on server socket, then restore to false */
assertEquals(ss.getUseClientMode(), false);
@@ -733,8 +732,7 @@ public void checkServerTrusted(X509Certificate[] xc,
this.ctx.init(km.getKeyManagers(), trustAllCerts, new SecureRandom());
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
ss.setWantClientAuth(true);
ss.setNeedClientAuth(true);
@@ -798,8 +796,7 @@ public void checkServerTrusted(X509Certificate[] xc,
this.ctx.init(km.getKeyManagers(), trustNoCerts, new SecureRandom());
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
ss.setWantClientAuth(true);
ss.setNeedClientAuth(true);
@@ -859,8 +856,7 @@ else if (!s.contains("_anon_") && nonAnonSuite == null) {
Security.setProperty("wolfjsse.enabledCipherSuites", "");
this.ctx = tf.createSSLContext("TLS", "wolfJSSE");
SSLServerSocket ss =
- (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ WolfSSLTestFactory.createServerSocket(ctx, 0);
/* Default enabled should not include anon */
String[] enabled = ss.getEnabledCipherSuites();
diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSessionTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSessionTest.java
index 43ad19de..1f89564c 100644
--- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSessionTest.java
+++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSessionTest.java
@@ -57,6 +57,7 @@
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
+import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.ServerSocket;
@@ -468,7 +469,7 @@ public void testLocalIdentityUpdatedWhenResumptionDeclinedSocket()
/* One listening port keeps the client session cache key the same,
* each accepted socket is wrapped with the chosen server context */
- try (ServerSocket listener = new ServerSocket(0)) {
+ try (ServerSocket listener = WolfSSLTestFactory.createServerSocket(0)) {
listener.setSoTimeout(10000);
clientKm.setForcedClientAlias("client");
@@ -533,7 +534,7 @@ public void testClosedSocketSessionReportsOwnLocalIdentity()
new AtomicReference();
/* Server socket loads "server" and completes a handshake */
- try (ServerSocket listener = new ServerSocket(0)) {
+ try (ServerSocket listener = WolfSSLTestFactory.createServerSocket(0)) {
listener.setSoTimeout(10000);
final int port = listener.getLocalPort();
ExecutorService es = Executors.newSingleThreadExecutor();
@@ -1671,11 +1672,11 @@ public void testGetSessionInSocketConnection() throws Exception {
ctx = tf.createSSLContext(protocol, engineProvider);
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
final SSLSocket server = (SSLSocket)ss.accept();
server.setNeedClientAuth(true);
diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketFactoryTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketFactoryTest.java
index dc45830d..97cc5d69 100644
--- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketFactoryTest.java
+++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketFactoryTest.java
@@ -109,6 +109,11 @@ public static void testSetupSocketFactory() throws NoSuchProviderException,
throw new Exception(e);
}
+ /* Start static lists empty so running this again in the same JVM does
+ * not add duplicate entries */
+ enabledProtocols.clear();
+ sockFactories.clear();
+
/* populate enabledProtocols */
for (int i = 0; i < allProtocols.length; i++) {
try {
diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java
index 70ee11c7..fa60fe64 100644
--- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java
+++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java
@@ -25,6 +25,7 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.BeforeClass;
+import org.junit.AfterClass;
import org.junit.rules.TestRule;
import static org.junit.Assert.*;
@@ -46,6 +47,7 @@
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.LockSupport;
import java.io.InputStream;
@@ -202,6 +204,12 @@ public static void testSetupSocketFactory() throws NoSuchProviderException,
Provider p = Security.getProvider("wolfJSSE");
assertNotNull(p);
+ /* Start static lists empty so running this again in the same JVM does
+ * not add duplicate entries */
+ enabledProtocols.clear();
+ sockFactories.clear();
+ socks.clear();
+
/* populate enabledProtocols */
for (int i = 0; i < allProtocols.length; i++) {
try {
@@ -267,6 +275,16 @@ public static void testSetupSocketFactory() throws NoSuchProviderException,
}
}
+ @AfterClass
+ public static void testCleanupSockets() {
+
+ /* Close connections opened by testSetupSocketFactory() */
+ for (SSLSocket s : socks) {
+ closeQuietly(s);
+ }
+ socks.clear();
+ }
+
@Test
public void testGetSupportedCipherSuites()
throws NoSuchProviderException, NoSuchAlgorithmException {
@@ -361,13 +379,13 @@ public void testServerUsesClientCipherSuitePreference() throws Exception {
};
/* --- Case 1: default (server order) --- */
- SSLServerSocket ss1 = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss1 = WolfSSLTestFactory.createServerSocket(ctx, 0);
ss1.setEnabledCipherSuites(serverSuites);
SSLSocket cs1 = (SSLSocket)ctx.getSocketFactory().createSocket();
cs1.setEnabledCipherSuites(clientSuites);
- cs1.connect(new InetSocketAddress(ss1.getLocalPort()));
+ cs1.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss1.getLocalPort()));
final SSLSocket server1 = (SSLSocket)ss1.accept();
@@ -396,8 +414,7 @@ public void testServerUsesClientCipherSuitePreference() throws Exception {
ss1.close();
/* --- Case 2: server honors client order --- */
- SSLServerSocket ss2 = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss2 = WolfSSLTestFactory.createServerSocket(ctx, 0);
ss2.setEnabledCipherSuites(serverSuites);
/* Do not honor local cipher suites preference */
@@ -407,7 +424,8 @@ public void testServerUsesClientCipherSuitePreference() throws Exception {
SSLSocket cs2 = (SSLSocket)ctx.getSocketFactory().createSocket();
cs2.setEnabledCipherSuites(clientSuites);
- cs2.connect(new InetSocketAddress(ss2.getLocalPort()));
+ cs2.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss2.getLocalPort()));
final SSLSocket server2 = (SSLSocket)ss2.accept();
@@ -504,15 +522,15 @@ private String tls12SocketHandshake(String[] srvSuites,
ExecutorService es = Executors.newSingleThreadExecutor();
try {
- ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
ss.setEnabledCipherSuites(srvSuites);
ss.setEnabledProtocols(new String[] { "TLSv1.2" });
cs = (SSLSocket)ctx.getSocketFactory().createSocket();
cs.setEnabledCipherSuites(cliSuites);
cs.setEnabledProtocols(new String[] { "TLSv1.2" });
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
final SSLSocket server0 = (SSLSocket)ss.accept();
server = server0;
@@ -616,7 +634,7 @@ public void testReadReturnsEndOfStreamAfterPeerClose() throws Exception {
final SSLContext ctx = tf.createSSLContext("TLSv1.2", "wolfJSSE");
final byte[] msg = "hello".getBytes(StandardCharsets.UTF_8);
- final ServerSocket ss = new ServerSocket(0);
+ final ServerSocket ss = WolfSSLTestFactory.createServerSocket(0);
SSLSocket cs = null;
ExecutorService es = Executors.newSingleThreadExecutor();
@@ -816,8 +834,7 @@ public void testEnabledSupportedCurvesProperty() throws Exception {
Security.setProperty("wolfjsse.enabledSupportedCurves", "");
/* create SSLServerSocket first to get ephemeral port */
- ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
TestArgs sArgs = new TestArgs(null, null, true, true, true, null);
TestArgs cArgs = new TestArgs(null, null, false, false, true, null);
@@ -866,8 +883,7 @@ public void testEnabledSupportedCurvesProperty() throws Exception {
"secp256r1");
/* create SSLServerSocket first to get ephemeral port */
- ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
TestArgs sArgs = new TestArgs(null, null, true, true, true, null);
TestArgs cArgs = new TestArgs(null, null, false, false, true, null);
@@ -916,8 +932,7 @@ public void testEnabledSupportedCurvesProperty() throws Exception {
"secp256r1, secp521r1");
/* create SSLServerSocket first to get ephemeral port */
- ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
TestArgs sArgs = new TestArgs(null, null, true, true, true, null);
TestArgs cArgs = new TestArgs(null, null, false, false, true, null);
@@ -968,8 +983,7 @@ public void testEnabledSupportedCurvesProperty() throws Exception {
"badone, badtwo");
/* create SSLServerSocket first to get ephemeral port */
- ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
TestArgs cArgs = new TestArgs(null, null, false, false, true, null);
@@ -1019,8 +1033,7 @@ public void testClientServerThreaded() throws Exception {
this.ctx = tf.createSSLContext("TLS", ctxProvider);
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
TestArgs sArgs = new TestArgs(null, null, true, true, true, null);
TestArgs cArgs = new TestArgs(null, null, false, false, true, null);
@@ -1072,8 +1085,7 @@ public void alpnClientServerRunner(TestArgs sArgs, TestArgs cArgs,
this.ctx = tf.createSSLContext("TLS", ctxProvider);
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
sDoneLatch = new CountDownLatch(1);
cDoneLatch = new CountDownLatch(1);
@@ -1270,8 +1282,8 @@ public void testAlpnSelectCallbackOpaqueNamesFromSunJSSE()
final boolean expectFail = choice.indexOf('\u0000') >= 0;
SSLContext srvCtx = tf.createSSLContext("TLS", ctxProvider);
SSLContext cliCtx = tf.createSSLContext("TLS", "SunJSSE");
- final SSLServerSocket ss = (SSLServerSocket)srvCtx
- .getServerSocketFactory().createServerSocket(0);
+ final SSLServerSocket ss =
+ WolfSSLTestFactory.createServerSocket(srvCtx, 0);
final AtomicReference> seen =
new AtomicReference>();
final AtomicReference srvErr =
@@ -1351,8 +1363,8 @@ public int getBoundPort() {
public void run() {
try {
SSLContext ctx = tf.createSSLContext("TLS", ctxProvider);
- SSLServerSocket ss = (SSLServerSocket)ctx
- .getServerSocketFactory().createServerSocket(serverPort);
+ SSLServerSocket ss =
+ WolfSSLTestFactory.createServerSocket(ctx, serverPort);
boundPort = ss.getLocalPort();
@@ -1426,7 +1438,8 @@ public void connect() throws Exception {
SSLSocket sock = (SSLSocket)ctx.getSocketFactory().
createSocket();
- sock.connect(new InetSocketAddress(serverPort));
+ sock.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ serverPort));
/* Do TLS handshake */
sock.startHandshake();
@@ -1567,11 +1580,12 @@ public void testPreConsumedSocket() throws Exception {
this.ctx = tf.createSSLContext("TLS", ctxProvider);
/* create plain TCP server socket */
- ServerSocket serverSock = new ServerSocket(0);
+ ServerSocket serverSock = WolfSSLTestFactory.createServerSocket(0);
/* connect TLS client to TCP server socket */
SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(serverSock.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ serverSock.getLocalPort()));
final Socket server = serverSock.accept();
@@ -1651,11 +1665,12 @@ public void testCreateSocketNullHost() throws Exception {
this.ctx = tf.createSSLContext("TLS", ctxProvider);
/* create new ServerSocket first to get ephemeral port */
- ServerSocket ss = new ServerSocket(0);
+ ServerSocket ss = WolfSSLTestFactory.createServerSocket(0);
/* create new Socket, connect() to server */
Socket cs = new Socket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
/* accept client connection, normal java.net.Socket */
final Socket socket = ss.accept();
@@ -1679,11 +1694,11 @@ public void testEnableSessionCreation() throws Exception {
this.ctx = tf.createSSLContext("TLS", ctxProvider);
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
/* test getter/setter on client socket */
assertEquals(cs.getEnableSessionCreation(), true);
@@ -1742,11 +1757,11 @@ public void testSetUseClientMode() throws Exception {
this.ctx = tf.createSSLContext("TLS", "wolfJSSE");
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
/* test getter/setter on client socket, then restore to true */
assertEquals(cs.getUseClientMode(), true);
@@ -1794,11 +1809,11 @@ public Void call() throws Exception {
ss.close();
/* calling setUseClientMode() after handshake should throw exception */
- ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
final SSLSocket server2 = (SSLSocket)ss.accept();
@@ -1893,11 +1908,11 @@ public void testAddHandshakeCompletedListener() throws Exception {
serverFlag = false;
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
HandshakeCompletedListener clientListener =
new HandshakeCompletedListener() {
@@ -2018,11 +2033,11 @@ public void testGetSession() throws Exception {
this.ctx = tf.createSSLContext("TLS", ctxProvider);
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
final SSLSocket server = (SSLSocket)ss.accept();
@@ -2071,11 +2086,11 @@ public Void call() throws Exception {
this.ctx = tf.createSSLContext("TLS", ctxProvider);
/* create SSLServerSocket first to get ephemeral port */
- ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
final SSLSocket server2 = (SSLSocket)ss.accept();
@@ -2416,11 +2431,11 @@ public void testSetNeedClientAuth() throws Exception {
this.ctx = tf.createSSLContext("TLSv1.2", ctxProvider);
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
final SSLSocket server = (SSLSocket)ss.accept();
server.setWantClientAuth(true);
@@ -2460,11 +2475,11 @@ public Void call() throws Exception {
tf.createTrustManager("SunX509", (String)null, ctxProvider),
tf.createKeyManager("SunX509", tf.serverJKS, ctxProvider));
- ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
final SSLSocket server2 = (SSLSocket)ss.accept();
server2.setWantClientAuth(true);
@@ -2512,11 +2527,11 @@ pass with setNeedClientAuth(false) */
tf.createTrustManager("SunX509", tf.caServerJKS, ctxProvider),
tf.createKeyManager("SunX509", tf.clientJKS, ctxProvider));
- ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
- .createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(srvCtx, 0);
cs = (SSLSocket)cliCtx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
final SSLSocket server3 = (SSLSocket)ss.accept();
server3.setWantClientAuth(false);
@@ -2575,10 +2590,10 @@ public void testWantClientAuthRejectsUntrustedClientCert()
tf.createTrustManager("SunX509", tf.caServerJKS, ctxProvider),
tf.createKeyManager("SunX509", tf.clientJKS, ctxProvider));
- SSLServerSocket ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(srvCtx, 0);
SSLSocket cs = (SSLSocket)cliCtx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
final SSLSocket server = (SSLSocket)ss.accept();
server.setWantClientAuth(true);
@@ -2624,10 +2639,10 @@ public Void call() throws Exception {
tf.createTrustManager("SunX509", tf.caServerJKS, ctxProvider),
tf.createKeyManager("SunX509", tf.clientJKS, ctxProvider));
- ss = (SSLServerSocket)srvCtx2.getServerSocketFactory()
- .createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(srvCtx2, 0);
cs = (SSLSocket)cliCtx2.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
final SSLSocket server2 = (SSLSocket)ss.accept();
server2.setWantClientAuth(true);
@@ -2748,11 +2763,11 @@ private void protocolConnectionTest(String protocol) throws Exception {
this.ctx = tf.createSSLContext(protocol, ctxProvider);
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
final SSLSocket server = (SSLSocket)ss.accept();
ExecutorService es = Executors.newSingleThreadExecutor();
@@ -2866,12 +2881,12 @@ private void protocolConnectionTestExtendedSocket(String protocol)
this.ctx = tf.createSSLContext(protocol, ctxProvider);
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
/* create Socket to wrap inside our ExtendedSocket */
Socket sock1 = new Socket();
- sock1.connect(new InetSocketAddress(ss.getLocalPort()));
+ sock1.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
/* create ExtendedSocket, tests non-Socket inside WolfSSLSocket */
ExtendedSocket sock = new ExtendedSocket(sock1);
@@ -2931,8 +2946,7 @@ public void testConnectionWithDisabledAlgorithms() throws Exception {
/* create SSLServerSocket first to get ephemeral port */
SSLServerSocket ss =
- (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ WolfSSLTestFactory.createServerSocket(ctx, 0);
SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
/* restrict to single protocol that is being disabled */
@@ -2943,7 +2957,8 @@ public void testConnectionWithDisabledAlgorithms() throws Exception {
enabledProtocols.get(i));
/* don't need server since should throw exception before */
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(
+ InetAddress.getLoopbackAddress(), ss.getLocalPort()));
try {
cs.startHandshake();
@@ -2998,8 +3013,7 @@ public void testSessionResumption() throws Exception {
/* create SSLServerSocket first to get ephemeral port */
final SSLServerSocket ss =
- (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ WolfSSLTestFactory.createServerSocket(ctx, 0);
SSLSocketFactory cliFactory = ctx.getSocketFactory();
@@ -3109,8 +3123,7 @@ public void testSessionResumptionHostKeyedNoHashCollision()
try {
this.ctx = tf.createSSLContext(protocol, ctxProvider);
- ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
final int port = ss.getLocalPort();
final SSLServerSocket fss = ss;
@@ -3248,8 +3261,7 @@ private boolean connectTwiceWithClosedSession(ClosedSessionCheck check)
this.ctx = tf.createSSLContext(protocol, ctxProvider);
cliSesCtx = ctx.getClientSessionContext();
- ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
final int port = ss.getLocalPort();
final SSLServerSocket fss = ss;
@@ -3420,8 +3432,7 @@ public void testSessionResumptionSysPropDisabled() throws Exception {
/* Create SSLServerSocket first to get ephemeral port */
final SSLServerSocket ss =
- (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ WolfSSLTestFactory.createServerSocket(ctx, 0);
SSLSocketFactory cliFactory = ctx.getSocketFactory();
@@ -3523,8 +3534,7 @@ public void testSessionResumptionWithTicketEnabled() throws Exception {
/* create SSLServerSocket first to get ephemeral port */
final SSLServerSocket ss =
- (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ WolfSSLTestFactory.createServerSocket(ctx, 0);
SSLSocketFactory cliFactory = ctx.getSocketFactory();
@@ -3606,11 +3616,11 @@ public void testDoubleSocketClose() throws Exception {
this.ctx = tf.createSSLContext(protocol, ctxProvider);
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
final SSLSocket server = (SSLSocket)ss.accept();
ExecutorService es = Executors.newSingleThreadExecutor();
@@ -3718,11 +3728,11 @@ public void testClientServerUsingSystemProperties() throws Exception {
ctx.init(null, null, null);
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
final SSLSocket server = (SSLSocket)ss.accept();
ExecutorService es = Executors.newSingleThreadExecutor();
@@ -3914,11 +3924,6 @@ public void testSocketCloseInterruptsWrite() throws Exception {
SSLServerSocket ss = null;
boolean passed = false;
- /* pipe() interrupt mechamism not implemented for Windows yet since
- * Windows does not support Unix/Linux pipe(). Re-enable this test
- * for Windows when that support has been added */
- Assume.assumeFalse(WolfSSLTestFactory.isWindows());
-
if (WolfSSL.TLSv12Enabled()) {
protocol = "TLSv1.2";
} else if (WolfSSL.TLSv11Enabled()) {
@@ -3932,11 +3937,11 @@ public void testSocketCloseInterruptsWrite() throws Exception {
this.ctx = tf.createSSLContext(protocol, ctxProvider);
/* create SSLServerSocket first to get ephemeral port */
- ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
final SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
final SSLSocket server = (SSLSocket)ss.accept();
final CountDownLatch closeLatch = new CountDownLatch(1);
@@ -3958,7 +3963,9 @@ public Void call() throws Exception {
/* Sleep so write thread has a chance to do some
* writing before interrupt */
Thread.sleep(1000);
- cs.setSoLinger(true, 5);
+ /* close() waits up to SO_LINGER for close_notify, which
+ * cannot be sent while the send buffer is full */
+ cs.setSoLinger(true, 1);
cs.close();
} catch (SSLException e) {
@@ -3993,11 +4000,15 @@ public Void call() throws Exception {
}
} catch (SocketException e) {
- /* We expect SocketException with this message, error if
- * different than expected */
+ /* We expect SocketException with one of these messages,
+ * error if different than expected. Where close() lingers
+ * on unsent data before the socket is released (Windows,
+ * JDK 12 and earlier), the write fails once the connection
+ * is reset and reports the socket as closed. */
String msg = e.getMessage();
if (msg == null ||
- !msg.contains("Socket fd closed during poll")) {
+ (!msg.contains("Socket fd closed during select/poll") &&
+ !msg.contains("Socket is closed"))) {
e.printStackTrace();
fail("Incorrect SocketException thrown by client");
throw e;
@@ -4032,11 +4043,6 @@ public void testSocketCloseInterruptsRead() throws Exception {
SSLServerSocket ss = null;
boolean passed = false;
- /* pipe() interrupt mechamism not implemented for Windows yet since
- * Windows does not support Unix/Linux pipe(). Re-enable this test
- * for Windows when that support has been added */
- Assume.assumeFalse(WolfSSLTestFactory.isWindows());
-
if (WolfSSL.TLSv12Enabled()) {
protocol = "TLSv1.2";
} else if (WolfSSL.TLSv11Enabled()) {
@@ -4050,11 +4056,11 @@ public void testSocketCloseInterruptsRead() throws Exception {
this.ctx = tf.createSSLContext(protocol, ctxProvider);
/* create SSLServerSocket first to get ephemeral port */
- ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
final SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
final SSLSocket server = (SSLSocket)ss.accept();
final CountDownLatch closeLatch = new CountDownLatch(1);
@@ -4075,7 +4081,7 @@ public Void call() throws Exception {
/* Sleep to let client thread hit read call */
Thread.sleep(1000);
- cs.setSoLinger(true, 5);
+ cs.setSoLinger(true, 1);
cs.close();
} catch (SSLException e) {
@@ -4140,6 +4146,349 @@ public Void call() throws Exception {
}
}
+ /* Read on an SSLSocket with setSoTimeout() set and a silent peer must
+ * throw SocketTimeoutException once the timeout has passed, and leave
+ * the socket usable for later reads. If layered is true, the SSLSocket
+ * is created over an already connected plain Socket, otherwise the
+ * SSLSocket is connected directly. wolfJSSE passes a different Socket
+ * to native setFd() in each case.
+ *
+ * On Windows this needs native setFd() to put the socket in non-blocking
+ * mode, otherwise the read waits inside recv() and the timeout is never
+ * applied. */
+ private void socketReadTimeoutTest(boolean layered) throws Exception {
+
+ final int timeoutMs = 1000;
+ final byte[] msg = "ping".getBytes(StandardCharsets.US_ASCII);
+ final CountDownLatch sendLatch = new CountDownLatch(1);
+ final CountDownLatch clientDone = new CountDownLatch(1);
+ byte[] buf = new byte[64];
+ SSLServerSocket ss = null;
+ SSLSocket cs = null;
+ SSLSocket server = null;
+ Socket plain = null;
+ ExecutorService es = null;
+ long start;
+ long elapsedMs;
+ int ret;
+
+ this.ctx = tf.createSSLContext("TLS", ctxProvider);
+
+ try {
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
+ InetSocketAddress addr = new InetSocketAddress(
+ InetAddress.getLoopbackAddress(), ss.getLocalPort());
+
+ if (layered) {
+ plain = new Socket();
+ plain.connect(addr);
+ cs = (SSLSocket)ctx.getSocketFactory().createSocket(
+ plain, addr.getHostString(), addr.getPort(), true);
+ }
+ else {
+ cs = (SSLSocket)ctx.getSocketFactory().createSocket();
+ cs.connect(addr);
+ }
+ server = (SSLSocket)ss.accept();
+
+ /* Server finishes the handshake, stays silent until released,
+ * then sends msg. Sends anyway after 20 sec, so a read() that
+ * never times out fails the test instead of hanging it. */
+ final SSLSocket srv = server;
+ es = Executors.newSingleThreadExecutor();
+ Future serverFuture = es.submit(new Callable() {
+ @Override
+ public Void call() throws Exception {
+ srv.startHandshake();
+ sendLatch.await(20, TimeUnit.SECONDS);
+ OutputStream out = srv.getOutputStream();
+ out.write(msg);
+ out.flush();
+
+ /* Keep socket open until client has read msg */
+ clientDone.await(20, TimeUnit.SECONDS);
+ return null;
+ }
+ });
+
+ cs.startHandshake();
+ InputStream in = cs.getInputStream();
+
+ /* Peer is silent, read() must time out */
+ cs.setSoTimeout(timeoutMs);
+ start = System.nanoTime();
+ try {
+ ret = in.read(buf, 0, buf.length);
+ fail("read() with setSoTimeout(" + timeoutMs + ") and a " +
+ "silent peer returned " + ret + " after " +
+ ((System.nanoTime() - start) / 1000000) + " ms, " +
+ "expected SocketTimeoutException");
+ } catch (SocketTimeoutException e) {
+ elapsedMs = (System.nanoTime() - start) / 1000000;
+ /* Allow for coarse select() timer resolution on Windows */
+ assertTrue("read() timed out early, after " + elapsedMs +
+ " ms", elapsedMs >= timeoutMs - 100);
+ assertTrue("read() timed out late, after " + elapsedMs +
+ " ms", elapsedMs < timeoutMs + 9000);
+ }
+
+ /* Socket is still usable after the timeout, and a timeout of 0
+ * waits for data with no limit */
+ cs.setSoTimeout(0);
+ sendLatch.countDown();
+ ret = in.read(buf, 0, buf.length);
+ assertEquals(msg.length, ret);
+ assertArrayEquals(msg, Arrays.copyOf(buf, ret));
+
+ clientDone.countDown();
+ serverFuture.get(10, TimeUnit.SECONDS);
+
+ } finally {
+ sendLatch.countDown();
+ clientDone.countDown();
+ closeQuietly(cs);
+ closeQuietly(plain);
+ closeQuietly(server);
+ closeQuietly(ss);
+ if (es != null) {
+ es.shutdownNow();
+ }
+ }
+ }
+
+ /* SSLSocket created with createSocket() then connect() */
+ @Test(timeout = 60000)
+ public void testSocketReadTimeout() throws Exception {
+ socketReadTimeoutTest(false);
+ }
+
+ /* SSLSocket layered over a connected plain Socket with
+ * createSocket(Socket, String, int, boolean), as used by Apache
+ * HttpClient */
+ @Test(timeout = 60000)
+ public void testLayeredSocketReadTimeout() throws Exception {
+ socketReadTimeoutTest(true);
+ }
+
+ /* startHandshake() with setSoTimeout() set must throw
+ * SocketTimeoutException when the peer accepts the TCP connection but
+ * never answers the ClientHello. */
+ @Test(timeout = 60000)
+ public void testSocketHandshakeTimeout() throws Exception {
+
+ final int timeoutMs = 1000;
+ final CountDownLatch clientDone = new CountDownLatch(1);
+ ServerSocket ss = null;
+ SSLSocket cs = null;
+ ExecutorService es = null;
+ long start;
+ long elapsedMs;
+
+ this.ctx = tf.createSSLContext("TLS", ctxProvider);
+
+ try {
+ ss = new ServerSocket(0, 1, InetAddress.getLoopbackAddress());
+ final ServerSocket srv = ss;
+
+ /* Plain TCP server that accepts, then never responds. Closes
+ * after 20 sec, so a handshake that never times out fails the
+ * test instead of hanging it. */
+ es = Executors.newSingleThreadExecutor();
+ Future serverFuture = es.submit(new Callable() {
+ @Override
+ public Void call() throws Exception {
+ Socket s = srv.accept();
+ try {
+ clientDone.await(20, TimeUnit.SECONDS);
+ } finally {
+ s.close();
+ }
+ return null;
+ }
+ });
+
+ cs = (SSLSocket)ctx.getSocketFactory().createSocket();
+ cs.connect(new InetSocketAddress(
+ InetAddress.getLoopbackAddress(), ss.getLocalPort()));
+ cs.setSoTimeout(timeoutMs);
+
+ start = System.nanoTime();
+ try {
+ cs.startHandshake();
+ fail("startHandshake() against a silent peer should time out");
+ } catch (SocketTimeoutException e) {
+ elapsedMs = (System.nanoTime() - start) / 1000000;
+ assertTrue("startHandshake() timed out early, after " +
+ elapsedMs + " ms", elapsedMs >= timeoutMs - 100);
+ assertTrue("startHandshake() timed out late, after " +
+ elapsedMs + " ms", elapsedMs < timeoutMs + 9000);
+ }
+
+ clientDone.countDown();
+ serverFuture.get(10, TimeUnit.SECONDS);
+
+ } finally {
+ clientDone.countDown();
+ closeQuietly(cs);
+ closeQuietly(ss);
+ if (es != null) {
+ es.shutdownNow();
+ }
+ }
+ }
+
+ /* close() on a TLS 1.3 client checks for a session ticket the peer may
+ * still send. It must not wait for one while closing, even if the peer
+ * stays silent after the handshake. getSession() is not called before
+ * close(), since that would run the same ticket check first. */
+ @Test(timeout = 60000)
+ public void testSocketCloseTls13ClientDoesNotWaitForTicket()
+ throws Exception {
+
+ SSLServerSocket ss = null;
+ SSLSocket cs = null;
+ SSLSocket server = null;
+ final CountDownLatch clientDone = new CountDownLatch(1);
+ ExecutorService es = Executors.newSingleThreadExecutor();
+ long start;
+ long closeMs;
+
+ Assume.assumeTrue(WolfSSL.TLSv13Enabled());
+
+ this.ctx = tf.createSSLContext("TLSv1.3", ctxProvider);
+
+ try {
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
+ cs = (SSLSocket)ctx.getSocketFactory().createSocket();
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
+ server = (SSLSocket)ss.accept();
+
+ /* Server completes the handshake, then stays silent */
+ final SSLSocket srv = server;
+ Future serverFuture = es.submit(new Callable() {
+ @Override
+ public Void call() throws Exception {
+ srv.startHandshake();
+ clientDone.await(20, TimeUnit.SECONDS);
+ return null;
+ }
+ });
+ cs.startHandshake();
+
+ start = System.nanoTime();
+ cs.close();
+ closeMs = (System.nanoTime() - start) / 1000000;
+ assertTrue("close() of TLS 1.3 client took " + closeMs + " ms",
+ closeMs < 1000);
+
+ clientDone.countDown();
+ serverFuture.get(10, TimeUnit.SECONDS);
+
+ } finally {
+ clientDone.countDown();
+ closeQuietly(cs);
+ closeQuietly(server);
+ closeQuietly(ss);
+ es.shutdownNow();
+ }
+ }
+
+ /* Close a client SSLSocket with SO_LINGER set to linger seconds while
+ * another thread is blocked in write() because the peer does not read.
+ * The blocked write() must fail once the socket is closed. Returns how
+ * long close() took, in milliseconds. */
+ private long closeWithLingerWhileWriteBlocked(int linger)
+ throws Exception {
+
+ SSLServerSocket ss = null;
+ SSLSocket cs = null;
+ SSLSocket server = null;
+ ExecutorService es = Executors.newSingleThreadExecutor();
+ final AtomicLong written = new AtomicLong(0);
+ long last;
+ long start;
+ long closeMs;
+
+ /* TLS 1.2 if available, since close() on a TLS 1.3 client can also
+ * wait for a session ticket, which would add to the time measured */
+ this.ctx = tf.createSSLContext(
+ WolfSSL.TLSv12Enabled() ? "TLSv1.2" : "TLS", ctxProvider);
+
+ try {
+ ss = (SSLServerSocket)ctx.getServerSocketFactory()
+ .createServerSocket(0, 0, InetAddress.getLoopbackAddress());
+ cs = (SSLSocket)ctx.getSocketFactory().createSocket();
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
+ server = (SSLSocket)ss.accept();
+
+ final SSLSocket srv = server;
+ Future serverFuture = es.submit(new Callable() {
+ @Override
+ public Void call() throws Exception {
+ srv.startHandshake();
+ return null;
+ }
+ });
+ cs.startHandshake();
+ serverFuture.get(10, TimeUnit.SECONDS);
+
+ /* Server never reads, so writes stop once buffers are full */
+ final OutputStream out = cs.getOutputStream();
+ Future writer = es.submit(new Callable() {
+ @Override
+ public Throwable call() {
+ byte[] buf = new byte[1024];
+ try {
+ while (true) {
+ out.write(buf);
+ written.addAndGet(buf.length);
+ }
+ } catch (Throwable t) {
+ return t;
+ }
+ }
+ });
+
+ /* Wait until write() has made no progress for 500 ms */
+ start = System.nanoTime();
+ do {
+ last = written.get();
+ Thread.sleep(500);
+ assertTrue("write() did not block within 20 sec",
+ (System.nanoTime() - start) < 20000000000L);
+ } while (written.get() != last);
+
+ cs.setSoLinger(true, linger);
+ start = System.nanoTime();
+ cs.close();
+ closeMs = (System.nanoTime() - start) / 1000000;
+
+ Throwable t = writer.get(10, TimeUnit.SECONDS);
+ assertTrue("blocked write() should fail with IOException after " +
+ "close(), got: " + t, t instanceof IOException);
+
+ return closeMs;
+
+ } finally {
+ closeQuietly(cs);
+ closeQuietly(server);
+ closeQuietly(ss);
+ es.shutdownNow();
+ }
+ }
+
+ /* SO_LINGER 0 means close without waiting, so close() must not wait for
+ * close_notify to be sent while the send buffer is full. */
+ @Test(timeout = 60000)
+ public void testSocketCloseLingerZeroDoesNotWait() throws Exception {
+
+ long closeMs = closeWithLingerWhileWriteBlocked(0);
+ assertTrue("close() with SO_LINGER 0 took " + closeMs + " ms",
+ closeMs < 2000);
+ }
+
/* Close a resource, ignoring exceptions, for deterministic
* per-iteration cleanup in the stress tests below. */
private static void closeQuietly(java.io.Closeable c) {
@@ -4200,11 +4549,6 @@ public void testSocketCloseDuringConcurrentWrite() throws Exception {
final int maxIterations = 300;
final long budgetMs = 5000;
- /* pipe() interrupt mechanism not implemented for Windows yet since
- * Windows does not support Unix/Linux pipe(). Re-enable this test
- * for Windows when that support has been added */
- Assume.assumeFalse(WolfSSLTestFactory.isWindows());
-
if (WolfSSL.TLSv12Enabled()) {
protocol = "TLSv1.2";
} else if (WolfSSL.TLSv13Enabled()) {
@@ -4251,8 +4595,7 @@ public void run() {
SSLSocket server = null;
try {
- ss = (SSLServerSocket)ctx
- .getServerSocketFactory().createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
/* autoClose false keeps underlying Socket open across
* SSLSocket.close() */
@@ -4383,11 +4726,6 @@ public void testSocketCloseDuringConcurrentRead() throws Exception {
final int maxIterations = 500;
final long budgetMs = 5000;
- /* pipe() interrupt mechanism not implemented for Windows yet since
- * Windows does not support Unix/Linux pipe(). Re-enable this test
- * for Windows when that support has been added */
- Assume.assumeFalse(WolfSSLTestFactory.isWindows());
-
if (WolfSSL.TLSv12Enabled()) {
protocol = "TLSv1.2";
} else if (WolfSSL.TLSv13Enabled()) {
@@ -4437,8 +4775,7 @@ public void run() {
final AtomicBoolean serverStop = new AtomicBoolean(false);
try {
- ss = (SSLServerSocket)ctx
- .getServerSocketFactory().createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
/* autoClose false keeps underlying Socket open across
* SSLSocket.close() */
@@ -4592,8 +4929,7 @@ public void testSocketCloseDuringWriteDoesNotLeakFds() throws Exception {
SSLSocket server = null;
try {
- ss = (SSLServerSocket)ctx
- .getServerSocketFactory().createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
plain = new Socket();
plain.connect(new InetSocketAddress("127.0.0.1",
ss.getLocalPort()));
@@ -4737,7 +5073,7 @@ public void testCloseAfterHandshakeFailDoesNotLeakFds() throws Exception {
try {
/* Plain (non-TLS) server that accepts then closes, so the
* client handshake fails after the streams are created. */
- ss = new ServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(0);
final ServerSocket srv = ss;
Thread acceptor = new Thread(new Runnable() {
@Override
@@ -4827,7 +5163,7 @@ public void testCloseOverClosedTransportDoesNotLeakFds()
SSLSocket cs = null;
try {
- ss = new ServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(0);
plain = new Socket();
plain.connect(new InetSocketAddress("127.0.0.1",
ss.getLocalPort()));
@@ -4900,8 +5236,7 @@ public void testCloseDuringActiveReadFreesPipe() throws Exception {
Future serverFuture = null;
try {
- ss = (SSLServerSocket)ctx
- .getServerSocketFactory().createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
plain = new Socket();
plain.connect(new InetSocketAddress("127.0.0.1",
ss.getLocalPort()));
@@ -5058,7 +5393,7 @@ private void checkCloseReportsFreeFailure(Throwable failure)
WolfSSLSession origSsl = null;
try {
- ss = new ServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(0);
plain = new Socket();
plain.connect(new InetSocketAddress("127.0.0.1",
ss.getLocalPort()));
@@ -5145,11 +5480,11 @@ public void testSocketMethodsAfterClose() throws Exception {
this.ctx = tf.createSSLContext(protocol, ctxProvider);
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
final SSLSocket server = (SSLSocket)ss.accept();
ExecutorService es = Executors.newSingleThreadExecutor();
@@ -5337,8 +5672,7 @@ public void testAutoSNIProperty() throws Exception {
this.ctx = tf.createSSLContext("TLS", ctxProvider);
/* Create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
/* Set up test arguments without explicit SNI configuration.
* With autoSNI=true, SNI should be automatically set based on
@@ -5382,8 +5716,7 @@ public void testAutoSNIProperty() throws Exception {
/* Test with autoSNI disabled */
System.setProperty("wolfjsse.autoSNI", "false");
- ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ ss = WolfSSLTestFactory.createServerSocket(ctx, 0);
sDoneLatch = new CountDownLatch(1);
cDoneLatch = new CountDownLatch(1);
@@ -5436,8 +5769,8 @@ public void testSNIMatchers() throws Exception {
this.ctx = tf.createSSLContext("TLS", ctxProvider);
/* create SSLServerSocket first to get ephemeral port */
- final SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
- .createServerSocket(0);
+ final SSLServerSocket ss =
+ WolfSSLTestFactory.createServerSocket(ctx, 0);
/* Configure SNI matcher for server*/
SNIMatcher matcher =
@@ -5455,7 +5788,8 @@ public void testSNIMatchers() throws Exception {
/* ------------------------------------ */
SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
/* Set SNI hostname for client */
SNIHostName serverName = new SNIHostName("www.example.com");
@@ -5491,7 +5825,8 @@ public Void call() throws Exception {
/* Test unmatched SNI case, should fail */
/* ------------------------------------ */
cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
/* Set non-matching SNI hostname for client */
serverName = new SNIHostName("www.example.org");
@@ -5553,8 +5888,8 @@ public void testReadDoesNotHangAfterFailedHandshake()
this.ctx = tf.createSSLContext("TLS", ctxProvider);
- final SSLServerSocket ss = (SSLServerSocket)ctx
- .getServerSocketFactory().createServerSocket(0);
+ final SSLServerSocket ss =
+ WolfSSLTestFactory.createServerSocket(ctx, 0);
/* Server accepts only SNI www.example.com */
SNIMatcher matcher =
@@ -5571,7 +5906,8 @@ public void testReadDoesNotHangAfterFailedHandshake()
try {
cs = (SSLSocket)ctx.getSocketFactory().createSocket();
- cs.connect(new InetSocketAddress(ss.getLocalPort()));
+ cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(),
+ ss.getLocalPort()));
/* Non-matching SNI makes the server throw SSLHandshakeException
* after the native handshake completes. */
@@ -5886,7 +6222,8 @@ public void run() {
SSLSocket sock = (SSLSocket)ctx.getSocketFactory()
.createSocket();
sock.setUseClientMode(true);
- sock.connect(new InetSocketAddress(srvPort));
+ sock.connect(new InetSocketAddress(
+ InetAddress.getLoopbackAddress(), srvPort));
SSLParameters params = sock.getSSLParameters();
@@ -6198,8 +6535,7 @@ public X509Certificate[] getAcceptedIssuers() {
serverTM.getTrustManagers(), null);
serverSocket =
- (SSLServerSocket)serverCtx.getServerSocketFactory()
- .createServerSocket(0);
+ WolfSSLTestFactory.createServerSocket(serverCtx, 0);
final int serverPort = serverSocket.getLocalPort();
final SSLServerSocket finalServerSocket = serverSocket;
@@ -6397,8 +6733,7 @@ public X509Certificate[] getAcceptedIssuers() {
cliCtx.init(null, rejectingTMs, null);
/* Create server socket on ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)srvCtx
- .getServerSocketFactory().createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(srvCtx, 0);
SSLSocket cs = (SSLSocket)cliCtx.getSocketFactory().createSocket();
cs.connect(new InetSocketAddress(
diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java
index 0d6ff131..90d86ac1 100644
--- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java
+++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLTestFactory.java
@@ -29,6 +29,8 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.Security;
@@ -47,12 +49,15 @@
import java.util.List;
import java.util.Arrays;
import java.util.Enumeration;
+import javax.net.ServerSocketFactory;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
@@ -1275,6 +1280,77 @@ protected static boolean isAndroid() {
return false;
}
+ /**
+ * Create a ServerSocket from the given factory, listening on all
+ * interfaces.
+ *
+ * For an ephemeral port (0), SO_REUSEADDR is disabled before binding.
+ * With it enabled, the Java default on non-Windows platforms, macOS can
+ * assign a port another process has bound on a specific address such as
+ * 127.0.0.1, so connections to that address reach the other process
+ * instead of this listener.
+ *
+ * @param factory factory to create the socket from
+ * @param port port to listen on, 0 for an ephemeral port
+ * @return bound ServerSocket
+ * @throws IOException on error creating or binding the socket
+ */
+ protected static ServerSocket createServerSocket(
+ ServerSocketFactory factory, int port) throws IOException {
+
+ ServerSocket ss = factory.createServerSocket();
+ if (port == 0) {
+ ss.setReuseAddress(false);
+ }
+ ss.bind(new InetSocketAddress(port));
+ return ss;
+ }
+
+ /**
+ * Create a plain ServerSocket, see
+ * createServerSocket(ServerSocketFactory, int).
+ *
+ * @param port port to listen on, 0 for an ephemeral port
+ * @return bound ServerSocket
+ * @throws IOException on error creating or binding the socket
+ */
+ protected static ServerSocket createServerSocket(int port)
+ throws IOException {
+
+ return createServerSocket(ServerSocketFactory.getDefault(), port);
+ }
+
+ /**
+ * Create an SSLServerSocket from the given factory, see
+ * createServerSocket(ServerSocketFactory, int).
+ *
+ * @param sf factory to create the socket from
+ * @param port port to listen on, 0 for an ephemeral port
+ * @return bound SSLServerSocket
+ * @throws IOException on error creating or binding the socket
+ */
+ protected static SSLServerSocket createServerSocket(
+ SSLServerSocketFactory sf, int port) throws IOException {
+
+ return (SSLServerSocket)createServerSocket(
+ (ServerSocketFactory)sf, port);
+ }
+
+ /**
+ * Create an SSLServerSocket from the given SSLContext, see
+ * createServerSocket(ServerSocketFactory, int).
+ *
+ * @param ctx SSLContext to create the socket from
+ * @param port port to listen on, 0 for an ephemeral port
+ * @return bound SSLServerSocket
+ * @throws IOException on error creating or binding the socket
+ */
+ protected static SSLServerSocket createServerSocket(SSLContext ctx,
+ int port) throws IOException {
+
+ return createServerSocket(ctx.getServerSocketFactory(), port);
+ }
+
/**
* Test if the env is Windows.
* @return true if Windows, otherwise false
diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLTrustX509Test.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLTrustX509Test.java
index 847fc896..23282bad 100644
--- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLTrustX509Test.java
+++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLTrustX509Test.java
@@ -57,6 +57,7 @@
import java.util.HashSet;
import java.util.Set;
import java.net.Socket;
+import java.net.InetAddress;
import java.net.InetSocketAddress;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
@@ -1568,8 +1569,7 @@ public void testUsingRsaPssCert()
tf.createTrustManager("SunX509", tf.caServerJKS, provider),
tf.createKeyManager("SunX509", tf.clientRSAPSSJKS, provider));
- SSLServerSocket ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(srvCtx, 0);
TestArgs serverArgs = new TestArgs(null, null, true, true, true, null);
TestSSLSocketServer server = new TestSSLSocketServer(
@@ -1669,8 +1669,7 @@ private void testX509ExtTrustMgrSSLSocketBasicSuccess()
tf.createKeyManager("SunX509", tf.clientJKS, provider));
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(srvCtx, 0);
TestArgs serverArgs = new TestArgs(null, null, true, true, true, null);
TestSSLSocketServer server = new TestSSLSocketServer(
@@ -1721,8 +1720,7 @@ private void testX509ExtTrustMgrSSLSocketNoStartHandshakeSuccess()
tf.createKeyManager("SunX509", tf.clientJKS, provider));
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(srvCtx, 0);
TestArgs serverArgs = new TestArgs(null, null, true, true, false, null);
TestSSLSocketServer server = new TestSSLSocketServer(
@@ -1773,8 +1771,7 @@ private void testX509ExtTrustMgrSSLSocketNoClientStartHandshakeSuccess()
tf.createKeyManager("SunX509", tf.clientJKS, provider));
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(srvCtx, 0);
TestArgs serverArgs = new TestArgs(null, null, true, true, true, null);
TestSSLSocketServer server = new TestSSLSocketServer(
@@ -1825,8 +1822,7 @@ private void testX509ExtTrustMgrSSLSocketNoServerStartHandshakeSuccess()
tf.createKeyManager("SunX509", tf.clientJKS, provider));
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(srvCtx, 0);
TestArgs serverArgs = new TestArgs(null, null, true, true, false, null);
TestSSLSocketServer server = new TestSSLSocketServer(
@@ -1877,8 +1873,7 @@ private void testX509ExtTrustMgrSSLSocketBasicFail()
tf.createKeyManager("SunX509", tf.clientJKS, provider));
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(srvCtx, 0);
TestArgs serverArgs = new TestArgs(null, null, true, true, true, null);
TestSSLSocketServer server = new TestSSLSocketServer(
@@ -1921,8 +1916,7 @@ private void testX509ExtTrustMgrSSLSocketEndpointAlgFail()
tf.createKeyManager("SunX509", tf.clientJKS, provider));
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(srvCtx, 0);
TestArgs serverArgs = new TestArgs(null, null, true, true, true, null);
TestSSLSocketServer server = new TestSSLSocketServer(
@@ -2451,8 +2445,7 @@ private Exception connectSSLSocketEndpointAlgo(KeyStore srvCertStore,
tf.createTrustManager("SunX509", srvCertStore, provider),
tf.createKeyManager("SunX509", tf.clientJKS, provider));
- SSLServerSocket ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(srvCtx, 0);
TestArgs serverArgs = new TestArgs(null, null, true, true, true, null);
TestSSLSocketServer server = new TestSSLSocketServer(
@@ -2664,8 +2657,8 @@ private void testX509ExtTrustMgrSSLSocketCertChangeSuccess()
tf.createKeyManager("SunX509", tf.clientJKS, provider));
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
- .createServerSocket(serverPort);
+ SSLServerSocket ss =
+ WolfSSLTestFactory.createServerSocket(srvCtx, serverPort);
serverPort = ss.getLocalPort();
serverArgs = new TestArgs(null, null, true, true, true, null);
@@ -2746,8 +2739,7 @@ private void testX509ExtTrustMgrSSLSocketCertChangeSuccess()
wctx.flushSessions((int)(Instant.now().getEpochSecond() + 1000));
wctx.free();
- ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
- .createServerSocket(serverPort);
+ ss = WolfSSLTestFactory.createServerSocket(srvCtx, serverPort);
serverArgs = new TestArgs(null, null, true, true, true, null);
server = new TestSSLSocketServer(srvCtx, ss, serverArgs, 1);
@@ -3328,8 +3320,7 @@ private void testX509ExtTrustMgrSSLSocketBasicExtSuccess()
tf.createKeyManager("SunX509", tf.clientJKS, provider));
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(srvCtx, 0);
TestArgs serverArgs = new TestArgs(null, null, true, true, true, null);
TestSSLSocketServer server = new TestSSLSocketServer(
@@ -3380,8 +3371,7 @@ private void testX509ExtTrustMgrSSLSocketBasicExtFail()
tf.createKeyManager("SunX509", tf.clientJKS, provider));
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(srvCtx, 0);
TestArgs serverArgs = new TestArgs(null, null, true, true, true, null);
TestSSLSocketServer server = new TestSSLSocketServer(
@@ -3423,8 +3413,7 @@ private void testX509ExtTrustMgrSSLSocketExtNoStartHandshakeSuccess()
tf.createKeyManager("SunX509", tf.clientJKS, provider));
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(srvCtx, 0);
TestArgs serverArgs = new TestArgs(null, null, true, true, false, null);
TestSSLSocketServer server = new TestSSLSocketServer(
@@ -3476,8 +3465,7 @@ private void testX509ExtTrustMgrSSLSocketExtNoClientStartHandshakeSuccess()
tf.createKeyManager("SunX509", tf.clientJKS, provider));
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(srvCtx, 0);
TestArgs serverArgs = new TestArgs(null, null, true, true, true, null);
TestSSLSocketServer server = new TestSSLSocketServer(
@@ -3529,8 +3517,7 @@ private void testX509ExtTrustMgrSSLSocketExtNoServerStartHandshakeSuccess()
tf.createKeyManager("SunX509", tf.clientJKS, provider));
/* create SSLServerSocket first to get ephemeral port */
- SSLServerSocket ss = (SSLServerSocket)srvCtx.getServerSocketFactory()
- .createServerSocket(0);
+ SSLServerSocket ss = WolfSSLTestFactory.createServerSocket(srvCtx, 0);
TestArgs serverArgs = new TestArgs(null, null, true, true, false, null);
TestSSLSocketServer server = new TestSSLSocketServer(
@@ -3802,7 +3789,8 @@ public void run() {
SSLSocket sock = (SSLSocket)ctx.getSocketFactory()
.createSocket();
sock.setUseClientMode(true);
- sock.connect(new InetSocketAddress(srvPort));
+ sock.connect(new InetSocketAddress(
+ InetAddress.getLoopbackAddress(), srvPort));
SSLParameters params = sock.getSSLParameters();
diff --git a/src/test/com/wolfssl/test/WolfSSLContextTest.java b/src/test/com/wolfssl/test/WolfSSLContextTest.java
index 62e27370..2e85ec39 100644
--- a/src/test/com/wolfssl/test/WolfSSLContextTest.java
+++ b/src/test/com/wolfssl/test/WolfSSLContextTest.java
@@ -1252,7 +1252,7 @@ public void test_WolfSSLContext_setVerifyHandshakeRouting()
cliCtxA.setVerify(WolfSSL.SSL_VERIFY_PEER, cbA);
cliCtxB.setVerify(WolfSSL.SSL_VERIFY_PEER, cbB);
- srvSocket = new ServerSocket(0);
+ srvSocket = WolfSSLTestCommon.createServerSocket(0);
srvSocket.setSoTimeout(10000);
final int port = srvSocket.getLocalPort();
final ServerSocket fSrvSock = srvSocket;
@@ -1500,7 +1500,7 @@ private int[] runPkCbHandshake(final WolfSSLContext srvCtx,
int cliRet;
try {
- srvSocket = new ServerSocket(0);
+ srvSocket = WolfSSLTestCommon.createServerSocket(0);
srvSocket.setSoTimeout(10000);
final ServerSocket fSrvSock = srvSocket;
es = Executors.newSingleThreadExecutor();
@@ -1951,7 +1951,7 @@ private void rsaCbHandshakeTls12() {
final TestRsaCbCtx srvVerifyCtx = new TestRsaCbCtx();
final TestRsaCbCtx cliVerifyCtx = new TestRsaCbCtx();
- srvSocket = new ServerSocket(0);
+ srvSocket = WolfSSLTestCommon.createServerSocket(0);
srvSocket.setSoTimeout(10000);
final int port = srvSocket.getLocalPort();
final ServerSocket fSrvSock = srvSocket;
@@ -2102,7 +2102,7 @@ private void rsaCbHandshakeTls13() {
final TestRsaCbCtx srvSignCtx = new TestRsaCbCtx();
final TestRsaCbCtx srvVerifyCtx = new TestRsaCbCtx();
- srvSocket = new ServerSocket(0);
+ srvSocket = WolfSSLTestCommon.createServerSocket(0);
srvSocket.setSoTimeout(10000);
final int port = srvSocket.getLocalPort();
final ServerSocket fSrvSock = srvSocket;
diff --git a/src/test/com/wolfssl/test/WolfSSLSessionTest.java b/src/test/com/wolfssl/test/WolfSSLSessionTest.java
index 96c6e091..9b1223fd 100644
--- a/src/test/com/wolfssl/test/WolfSSLSessionTest.java
+++ b/src/test/com/wolfssl/test/WolfSSLSessionTest.java
@@ -51,6 +51,7 @@
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.concurrent.CountDownLatch;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
@@ -615,7 +616,7 @@ public void test_WolfSSLSession_resumeWithSessionTickets()
WolfSSLContext cliCtx;
/* Create ServerSocket first to get ephemeral port */
- final ServerSocket srvSocket = new ServerSocket(0);
+ final ServerSocket srvSocket = WolfSSLTestCommon.createServerSocket(0);
final int port = srvSocket.getLocalPort();
srvCtx = createAndSetupWolfSSLContext(srvCert, srvKey,
@@ -829,7 +830,7 @@ private void assertHandshakeFailsWithVerifyCallback(
new WolfSSL();
try {
- srvSocket = new ServerSocket(0);
+ srvSocket = WolfSSLTestCommon.createServerSocket(0);
srvSocket.setSoTimeout(10000);
int port = srvSocket.getLocalPort();
@@ -1016,7 +1017,7 @@ public void test_WolfSSLSession_getDhKeySizeAfterDheHandshake()
cliCert, cliKey, WolfSSL.SSL_FILETYPE_PEM, caCert,
WolfSSL.TLSv1_2_ClientMethod());
- final ServerSocket srvSocket = new ServerSocket(0);
+ final ServerSocket srvSocket = WolfSSLTestCommon.createServerSocket(0);
final int port = srvSocket.getLocalPort();
Socket cliSock = null;
WolfSSLSession ssl = null;
@@ -1135,7 +1136,7 @@ public void test_WolfSSLSession_getErrorAfterCipherSuiteMismatch()
assertEquals("client setCipherList", WolfSSL.SSL_SUCCESS,
cliCtx.setCipherList("ECDHE-RSA-AES256-GCM-SHA384"));
- srvSocket = new ServerSocket(0);
+ srvSocket = WolfSSLTestCommon.createServerSocket(0);
srvSocket.setSoTimeout(10000);
es = Executors.newSingleThreadExecutor();
srvFuture = runOneShotTlsServer(srvSocket, srvCtx, es);
@@ -1504,6 +1505,47 @@ public void test_WolfSSLSession_freeSSL()
}
}
+ @Test
+ public void test_WolfSSLSession_restoreFdBlocking() throws Exception {
+
+ WolfSSLSession ssl = null;
+ ServerSocket srvSock = null;
+ Socket cliSock = null;
+
+ try {
+ ssl = new WolfSSLSession(ctx);
+
+ /* No socket set yet, nothing to restore */
+ assertEquals(WolfSSL.SSL_SUCCESS, ssl.restoreFdBlocking());
+
+ srvSock = new ServerSocket(0, 0, InetAddress.getLoopbackAddress());
+ cliSock = new Socket(InetAddress.getLoopbackAddress(),
+ srvSock.getLocalPort());
+ assertEquals(WolfSSL.SSL_SUCCESS, ssl.setFd(cliSock));
+ assertEquals(WolfSSL.SSL_SUCCESS, ssl.restoreFdBlocking());
+
+ ssl.freeSSL();
+ try {
+ ssl.restoreFdBlocking();
+ fail("restoreFdBlocking() after freeSSL() should throw");
+ } catch (IllegalStateException e) {
+ /* expected */
+ }
+ ssl = null;
+
+ } finally {
+ if (ssl != null) {
+ ssl.freeSSL();
+ }
+ if (cliSock != null) {
+ cliSock.close();
+ }
+ if (srvSock != null) {
+ srvSock.close();
+ }
+ }
+ }
+
/* Run one client/server TLS handshake over localhost, with the server
* using the provided ALPN select callback and the client offering
* cliProtos via useALPN(). Returns int[2] containing the server
@@ -1529,7 +1571,7 @@ private int[] runAlpnSelectHandshake(final WolfSSLALPNSelectCallback cb,
try {
/* Create ServerSocket first to get ephemeral port, set
* 10 sec timeout on accept() to prevent test hangs. */
- srvSocket = new ServerSocket(0);
+ srvSocket = WolfSSLTestCommon.createServerSocket(0);
srvSocket.setSoTimeout(10000);
final int port = srvSocket.getLocalPort();
@@ -1924,7 +1966,7 @@ public void test_WolfSSLSession_UseAfterFree()
try {
/* Create ServerSocket first to get ephemeral port,
* set 10 sec timeout on accept() to prevent test hangs. */
- srvSocket = new ServerSocket(0);
+ srvSocket = WolfSSLTestCommon.createServerSocket(0);
srvSocket.setSoTimeout(10000);
final int port = srvSocket.getLocalPort();
@@ -2084,7 +2126,7 @@ public void test_WolfSSLSession_getSessionID()
try {
/* Create ServerSocket first to get ephemeral port, set
* 10 sec timeout on accept() to prevent test hangs. */
- srvSocket = new ServerSocket(0);
+ srvSocket = WolfSSLTestCommon.createServerSocket(0);
srvSocket.setSoTimeout(10000);
final int port = srvSocket.getLocalPort();
@@ -2246,7 +2288,7 @@ public void test_WolfSSLSession_getPeerCertificateChainDER()
try {
/* Create ServerSocket first to get ephemeral port, set
* 10 sec timeout on accept() to prevent test hangs. */
- srvSocket = new ServerSocket(0);
+ srvSocket = WolfSSLTestCommon.createServerSocket(0);
srvSocket.setSoTimeout(10000);
final int port = srvSocket.getLocalPort();
@@ -2617,7 +2659,8 @@ public void test_WolfSSLSession_connectionWithDebug() throws Exception {
try {
/* Create ServerSocket first to get ephemeral port */
- final ServerSocket srvSocket = new ServerSocket(0);
+ final ServerSocket srvSocket =
+ WolfSSLTestCommon.createServerSocket(0);
srvCtx = createAndSetupWolfSSLContext(srvCert, srvKey,
WolfSSL.SSL_FILETYPE_PEM, cliCert,
@@ -2886,7 +2929,7 @@ public void test_WolfSSLSession_getSetSession() throws Exception {
try {
/* Create ServerSocket first to get ephemeral port, set 10 sec
* timeout on accept() to prevent test hangs. */
- srvSocket = new ServerSocket(0);
+ srvSocket = WolfSSLTestCommon.createServerSocket(0);
srvSocket.setSoTimeout(10000);
final ServerSocket finalSrvSocket = srvSocket;
@@ -3199,7 +3242,7 @@ private void runClientServerOneResumption() throws Exception {
WolfSSLContext cliCtx;
/* Create ServerSocket first to get ephemeral port */
- final ServerSocket srvSocket = new ServerSocket(0);
+ final ServerSocket srvSocket = WolfSSLTestCommon.createServerSocket(0);
srvCtx = createAndSetupWolfSSLContext(srvCert, srvKey,
WolfSSL.SSL_FILETYPE_PEM, cliCert,
@@ -3513,7 +3556,7 @@ public void test_WolfSSLSession_byteBufferPoolBufferSize() throws Exception {
public void test_WolfSSLSession_readBadArgsConsumesNothing()
throws Exception {
- final ServerSocket srvSocket = new ServerSocket(0);
+ final ServerSocket srvSocket = WolfSSLTestCommon.createServerSocket(0);
final WolfSSLContext srvCtx = createAndSetupWolfSSLContext(
srvCert, srvKey, WolfSSL.SSL_FILETYPE_PEM, cliCert,
WolfSSL.SSLv23_ServerMethod());
@@ -3818,7 +3861,7 @@ public void test_WolfSSLSession_ioBuffers() throws Exception {
WolfSSL lib = new WolfSSL();
assertNotNull(lib);
/* Create ServerSocket first to get ephemeral port */
- final ServerSocket srvSocket = new ServerSocket(0);
+ final ServerSocket srvSocket = WolfSSLTestCommon.createServerSocket(0);
srvCtx = createAndSetupWolfSSLContext(srvCert, srvKey,
WolfSSL.SSL_FILETYPE_PEM, cliCert,
@@ -4025,7 +4068,7 @@ public void test_WolfSSLSession_readByteBuffer() throws Exception {
WolfSSLContext cliCtx;
/* Create ServerSocket first to get ephemeral port */
- final ServerSocket srvSocket = new ServerSocket(0);
+ final ServerSocket srvSocket = WolfSSLTestCommon.createServerSocket(0);
srvCtx = createAndSetupWolfSSLContext(srvCert, srvKey,
WolfSSL.SSL_FILETYPE_PEM, cliCert,
@@ -4203,7 +4246,7 @@ public void test_WolfSSLSession_readWriteBoundsCheck() throws Exception {
WolfSSLContext cliCtx;
/* Create ServerSocket first to get ephemeral port */
- final ServerSocket srvSocket = new ServerSocket(0);
+ final ServerSocket srvSocket = WolfSSLTestCommon.createServerSocket(0);
srvCtx = createAndSetupWolfSSLContext(srvCert, srvKey,
WolfSSL.SSL_FILETYPE_PEM, cliCert, WolfSSL.SSLv23_ServerMethod());
@@ -4360,7 +4403,7 @@ public Void call() throws Exception {
public void test_WolfSSLSession_writeOutOfBoundsSendsNothing()
throws Exception {
- final ServerSocket srvSocket = new ServerSocket(0);
+ final ServerSocket srvSocket = WolfSSLTestCommon.createServerSocket(0);
final WolfSSLContext srvCtx = createAndSetupWolfSSLContext(
srvCert, srvKey, WolfSSL.SSL_FILETYPE_PEM, cliCert,
WolfSSL.SSLv23_ServerMethod());
@@ -4516,7 +4559,7 @@ public void test_WolfSSLSession_sessionToDerFromDer() throws Exception {
WolfSSLContext cliCtx;
/* Create ServerSocket first to get ephemeral port */
- final ServerSocket srvSocket = new ServerSocket(0);
+ final ServerSocket srvSocket = WolfSSLTestCommon.createServerSocket(0);
srvCtx = createAndSetupWolfSSLContext(srvCert, srvKey,
WolfSSL.SSL_FILETYPE_PEM, cliCert,
@@ -6069,7 +6112,7 @@ public void test_WolfSSLSession_dtlsCidDataExchangeAfterHandshake()
* not backing[pos). */
@Test
public void test_WolfSSLSession_readSlicedByteBuffer() throws Exception {
- final ServerSocket srvSocket = new ServerSocket(0);
+ final ServerSocket srvSocket = WolfSSLTestCommon.createServerSocket(0);
final WolfSSLContext srvCtx = createAndSetupWolfSSLContext(
srvCert, srvKey, WolfSSL.SSL_FILETYPE_PEM, cliCert,
WolfSSL.SSLv23_ServerMethod());
@@ -6440,7 +6483,7 @@ public void test_WolfSSLSession_setVerifyHandshakeCallback()
Future srvFuture = null;
try {
- srvSocket = new ServerSocket(0);
+ srvSocket = WolfSSLTestCommon.createServerSocket(0);
final int port = srvSocket.getLocalPort();
srvCtx = createAndSetupWolfSSLContext(srvCert, srvKey,
@@ -6612,13 +6655,8 @@ public void test_WolfSSLSession_shutdownBeforeHandshakeDone()
boolean shutdownHung = false;
final CountDownLatch clientDone = new CountDownLatch(1);
- /* setFd() leaves the socket blocking on Windows, so connect() can
- * not time out mid-handshake */
- Assume.assumeFalse("setFd() socket is blocking on Windows",
- WolfSSLTestCommon.isWindows());
-
try {
- srvSocket = new ServerSocket(0);
+ srvSocket = WolfSSLTestCommon.createServerSocket(0);
srvSocket.setSoTimeout(10000);
final ServerSocket srv = srvSocket;
@@ -6702,6 +6740,270 @@ public Void call() throws Exception {
}
}
+ /**
+ * read() with a timeout must throw SocketTimeoutException when the peer
+ * sends nothing, and the session must still read data sent afterwards.
+ *
+ * On Windows this needs setFd() to put the socket in non-blocking mode,
+ * otherwise wolfSSL_read() waits inside recv() and the timeout is never
+ * applied. SOCKET values on Windows are normally larger than FD_SETSIZE,
+ * so this also covers the socketSelect() descriptor check there.
+ */
+ @Test
+ public void test_WolfSSLSession_readTimeout() throws Exception {
+
+ final int timeoutMs = 1000;
+ final byte[] msg = "ping".getBytes(StandardCharsets.US_ASCII);
+ final CountDownLatch sendLatch = new CountDownLatch(1);
+ final CountDownLatch clientDone = new CountDownLatch(1);
+ ServerSocket srvSocket = null;
+ Socket cliSock = null;
+ WolfSSLContext srvCtx = null;
+ WolfSSLContext cliCtx = null;
+ WolfSSLSession ssl = null;
+ ExecutorService es = null;
+ byte[] buf = new byte[64];
+ long start;
+ long elapsedMs;
+ int ret;
+
+ try {
+ srvSocket = WolfSSLTestCommon.createServerSocket(0);
+ srvSocket.setSoTimeout(10000);
+ final ServerSocket srv = srvSocket;
+
+ srvCtx = createAndSetupWolfSSLContext(srvCert, srvKey,
+ WolfSSL.SSL_FILETYPE_PEM, cliCert,
+ WolfSSL.SSLv23_ServerMethod());
+ cliCtx = createAndSetupWolfSSLContext(cliCert, cliKey,
+ WolfSSL.SSL_FILETYPE_PEM, caCert,
+ WolfSSL.SSLv23_ClientMethod());
+ final WolfSSLContext sCtx = srvCtx;
+
+ /* Server finishes the handshake, stays silent until released,
+ * then sends msg. Sends anyway after 20 sec, so a read() that
+ * never times out fails the test instead of hanging it. */
+ es = Executors.newSingleThreadExecutor();
+ Future srvFuture = es.submit(new Callable() {
+ @Override
+ public Void call() throws Exception {
+ WolfSSLSession srvSes = null;
+ try (Socket s = srv.accept()) {
+ srvSes = new WolfSSLSession(sCtx);
+ assertEquals(WolfSSL.SSL_SUCCESS, srvSes.setFd(s));
+ assertEquals(WolfSSL.SSL_SUCCESS,
+ srvSes.accept(10000));
+ sendLatch.await(20, TimeUnit.SECONDS);
+ assertEquals(msg.length,
+ srvSes.write(msg, msg.length, 10000));
+
+ /* Keep socket open until client has read msg */
+ clientDone.await(20, TimeUnit.SECONDS);
+ return null;
+ } finally {
+ if (srvSes != null) {
+ srvSes.freeSSL();
+ }
+ }
+ }
+ });
+
+ cliSock = new Socket(InetAddress.getLoopbackAddress(),
+ srvSocket.getLocalPort());
+ ssl = new WolfSSLSession(cliCtx);
+ assertEquals(WolfSSL.SSL_SUCCESS, ssl.setFd(cliSock));
+ ret = ssl.connect(10000);
+ assertEquals("connect() failed, err = " + ssl.getError(ret),
+ WolfSSL.SSL_SUCCESS, ret);
+
+ /* Peer is silent, read() must time out */
+ start = System.nanoTime();
+ try {
+ ret = ssl.read(buf, 0, buf.length, timeoutMs);
+ fail("read() with " + timeoutMs + " ms timeout and a " +
+ "silent peer returned " + ret + " after " +
+ ((System.nanoTime() - start) / 1000000) + " ms, " +
+ "expected SocketTimeoutException");
+ } catch (SocketTimeoutException e) {
+ elapsedMs = (System.nanoTime() - start) / 1000000;
+ /* Allow for coarse select() timer resolution on Windows */
+ assertTrue("read() timed out early, after " + elapsedMs +
+ " ms", elapsedMs >= timeoutMs - 100);
+ assertTrue("read() timed out late, after " + elapsedMs +
+ " ms", elapsedMs < timeoutMs + 9000);
+ }
+
+ /* Session is still usable after the timeout */
+ sendLatch.countDown();
+ ret = ssl.read(buf, 0, buf.length, 10000);
+ assertEquals(msg.length, ret);
+ assertArrayEquals(msg, Arrays.copyOf(buf, ret));
+
+ clientDone.countDown();
+ srvFuture.get(10, TimeUnit.SECONDS);
+
+ } finally {
+ sendLatch.countDown();
+ clientDone.countDown();
+ if (ssl != null) {
+ ssl.freeSSL();
+ }
+ if (cliSock != null) {
+ cliSock.close();
+ }
+ if (srvSocket != null) {
+ srvSocket.close();
+ }
+ if (es != null) {
+ es.shutdown();
+ es.awaitTermination(10, TimeUnit.SECONDS);
+ }
+ if (cliCtx != null) {
+ cliCtx.free();
+ }
+ if (srvCtx != null) {
+ srvCtx.free();
+ }
+ }
+ }
+
+ /**
+ * A read() waiting for data when close() starts, which calls
+ * interruptBlockedIO() then restoreFdBlocking(), must not use the socket
+ * again once woken. Partial TLS record data from the peer would otherwise
+ * leave wolfSSL_read() waiting in recv() on a socket that is blocking
+ * again on Windows, with no timeout.
+ */
+ @Test
+ public void test_WolfSSLSession_readAfterRestoreFdBlocking()
+ throws Exception {
+
+ final CountDownLatch sendLatch = new CountDownLatch(1);
+ final CountDownLatch clientDone = new CountDownLatch(1);
+ ServerSocket srvSocket = null;
+ Socket cliSock = null;
+ WolfSSLContext srvCtx = null;
+ WolfSSLContext cliCtx = null;
+ WolfSSLSession ssl = null;
+ ExecutorService es = null;
+ ExecutorService readEs = null;
+ int ret;
+
+ try {
+ srvSocket = new ServerSocket(0, 0,
+ InetAddress.getLoopbackAddress());
+ srvSocket.setSoTimeout(10000);
+ final ServerSocket srv = srvSocket;
+
+ srvCtx = createAndSetupWolfSSLContext(srvCert, srvKey,
+ WolfSSL.SSL_FILETYPE_PEM, cliCert,
+ WolfSSL.SSLv23_ServerMethod());
+ cliCtx = createAndSetupWolfSSLContext(cliCert, cliKey,
+ WolfSSL.SSL_FILETYPE_PEM, caCert,
+ WolfSSL.SSLv23_ClientMethod());
+ final WolfSSLContext sCtx = srvCtx;
+
+ /* Server finishes the handshake, then when released sends the
+ * first byte of a TLS record header directly on the socket */
+ es = Executors.newSingleThreadExecutor();
+ Future srvFuture = es.submit(new Callable() {
+ @Override
+ public Void call() throws Exception {
+ WolfSSLSession srvSes = null;
+ try (Socket s = srv.accept()) {
+ srvSes = new WolfSSLSession(sCtx);
+ assertEquals(WolfSSL.SSL_SUCCESS, srvSes.setFd(s));
+ assertEquals(WolfSSL.SSL_SUCCESS, srvSes.accept(10000));
+ sendLatch.await(20, TimeUnit.SECONDS);
+ s.getOutputStream().write(0x17);
+ s.getOutputStream().flush();
+
+ clientDone.await(20, TimeUnit.SECONDS);
+ return null;
+ } finally {
+ if (srvSes != null) {
+ srvSes.freeSSL();
+ }
+ }
+ }
+ });
+
+ cliSock = new Socket(InetAddress.getLoopbackAddress(),
+ srvSocket.getLocalPort());
+ ssl = new WolfSSLSession(cliCtx);
+ assertEquals(WolfSSL.SSL_SUCCESS, ssl.setFd(cliSock));
+ ret = ssl.connect(10000);
+ assertEquals("connect() failed, err = " + ssl.getError(ret),
+ WolfSSL.SSL_SUCCESS, ret);
+
+ final WolfSSLSession cliSes = ssl;
+ readEs = Executors.newSingleThreadExecutor();
+ Future readFuture = readEs.submit(
+ new Callable() {
+ @Override
+ public Exception call() {
+ byte[] buf = new byte[64];
+ try {
+ cliSes.read(buf, 0, buf.length, 20000);
+ return null;
+ } catch (Exception e) {
+ return e;
+ }
+ }
+ });
+
+ /* Let read() reach select()/poll(), then start close */
+ Thread.sleep(500);
+ assertEquals(WolfSSL.SSL_SUCCESS, ssl.interruptBlockedIO());
+ assertEquals(WolfSSL.SSL_SUCCESS, ssl.restoreFdBlocking());
+ sendLatch.countDown();
+
+ Exception readEx = null;
+ try {
+ readEx = readFuture.get(5, TimeUnit.SECONDS);
+ } catch (TimeoutException e) {
+ fail("read() did not return after interruptBlockedIO() " +
+ "and restoreFdBlocking()");
+ }
+ assertNotNull("read() should throw once close has started",
+ readEx);
+ assertTrue("Expected SocketException, got: " + readEx,
+ (readEx instanceof SocketException) &&
+ readEx.getMessage().contains("fd closed during"));
+
+ clientDone.countDown();
+ srvFuture.get(10, TimeUnit.SECONDS);
+
+ } finally {
+ sendLatch.countDown();
+ clientDone.countDown();
+ /* Close socket first to release a read() stuck in recv() */
+ if (cliSock != null) {
+ cliSock.close();
+ }
+ if (readEs != null) {
+ readEs.shutdown();
+ readEs.awaitTermination(10, TimeUnit.SECONDS);
+ }
+ if (ssl != null) {
+ ssl.freeSSL();
+ }
+ if (srvSocket != null) {
+ srvSocket.close();
+ }
+ if (es != null) {
+ es.shutdown();
+ es.awaitTermination(10, TimeUnit.SECONDS);
+ }
+ if (cliCtx != null) {
+ cliCtx.free();
+ }
+ if (srvCtx != null) {
+ srvCtx.free();
+ }
+ }
+ }
+
/* I/O callbacks that record sent bytes. Sends return WANT_WRITE once
* the given number of sends have succeeded, until unblock() is called.
* Receives always return WANT_READ. */
@@ -6865,7 +7167,7 @@ public void test_WolfSSLSession_sendUserCanceledBeforeHandshake()
final CountDownLatch clientDone = new CountDownLatch(1);
try {
- srvSocket = new ServerSocket(0);
+ srvSocket = WolfSSLTestCommon.createServerSocket(0);
srvSocket.setSoTimeout(10000);
final ServerSocket srv = srvSocket;
diff --git a/src/test/com/wolfssl/test/WolfSSLTestCommon.java b/src/test/com/wolfssl/test/WolfSSLTestCommon.java
index d6fd2265..38aa167f 100644
--- a/src/test/com/wolfssl/test/WolfSSLTestCommon.java
+++ b/src/test/com/wolfssl/test/WolfSSLTestCommon.java
@@ -22,6 +22,9 @@
package com.wolfssl.test;
import java.io.File;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
import com.wolfssl.WolfSSLException;
@@ -89,4 +92,28 @@ public static byte[][] getFfdhe2048Params() {
}
return new byte[][] { p, new byte[] { 2 } };
}
+
+ /**
+ * Create a ServerSocket listening on all interfaces.
+ *
+ * For an ephemeral port (0), SO_REUSEADDR is disabled before binding.
+ * With it enabled, the Java default on non-Windows platforms, macOS can
+ * assign a port another process has bound on a specific address such as
+ * 127.0.0.1, so connections to that address reach the other process
+ * instead of this listener.
+ *
+ * @param port port to listen on, 0 for an ephemeral port
+ * @return bound ServerSocket
+ * @throws IOException on error creating or binding the socket
+ */
+ public static ServerSocket createServerSocket(int port)
+ throws IOException {
+
+ ServerSocket ss = new ServerSocket();
+ if (port == 0) {
+ ss.setReuseAddress(false);
+ }
+ ss.bind(new InetSocketAddress(port));
+ return ss;
+ }
}