From ebff82d8fb78164af9317505afc62eec6dab65a1 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 22 Oct 2026 09:24:03 -0600 Subject: [PATCH 1/5] JNI/JSSE: Set socket non-blocking on Windows so setSoTimeout() is honored --- README.md | 4 +- native/com_wolfssl_WolfSSLSession.c | 65 +++++- native/com_wolfssl_WolfSSLSession.h | 8 + src/java/com/wolfssl/WolfSSLSession.java | 31 +++ .../wolfssl/provider/jsse/WolfSSLSocket.java | 16 ++ .../provider/jsse/test/WolfSSLSocketTest.java | 192 ++++++++++++++++++ .../com/wolfssl/test/WolfSSLSessionTest.java | 173 +++++++++++++++- 7 files changed, 473 insertions(+), 16 deletions(-) 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_WolfSSLSession.c b/native/com_wolfssl_WolfSSLSession.c index 1b7ca84e..4e2660e9 100644 --- a/native/com_wolfssl_WolfSSLSession.c +++ b/native/com_wolfssl_WolfSSLSession.c @@ -518,7 +518,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 +630,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 +847,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 +899,21 @@ 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; + } +#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 { @@ -6944,6 +6962,33 @@ 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; + unsigned long nonBlocking = 0; + WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr; +#endif + (void)jenv; + (void)jcl; + +#ifdef USE_WINDOWS_API + /* Undo FIONBIO set in setFd(). Winsock closesocket() on a non-blocking + * SOCKET with SO_LINGER enabled fails with WSAEWOULDBLOCK and leaves the + * SOCKET open. */ + fd = wolfSSL_get_fd(ssl); + if ((fd >= 0) && + (ioctlsocket(fd, FIONBIO, &nonBlocking) == SOCKET_ERROR)) { + return SSL_FAILURE; + } +#else + (void)sslPtr; +#endif + + return SSL_SUCCESS; +} + 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/WolfSSLSession.java b/src/java/com/wolfssl/WolfSSLSession.java index e35a7d62..bc1a63c5 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); @@ -5767,6 +5768,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..4adea6fb 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java @@ -2396,6 +2396,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 diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java index 70ee11c7..5d4de5f5 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java @@ -4140,6 +4140,198 @@ 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 = (SSLServerSocket)ctx.getServerSocketFactory() + .createServerSocket(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 a resource, ignoring exceptions, for deterministic * per-iteration cleanup in the stress tests below. */ private static void closeQuietly(java.io.Closeable c) { diff --git a/src/test/com/wolfssl/test/WolfSSLSessionTest.java b/src/test/com/wolfssl/test/WolfSSLSessionTest.java index 96c6e091..57938396 100644 --- a/src/test/com/wolfssl/test/WolfSSLSessionTest.java +++ b/src/test/com/wolfssl/test/WolfSSLSessionTest.java @@ -1504,6 +1504,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 @@ -6612,11 +6653,6 @@ 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.setSoTimeout(10000); @@ -6702,6 +6738,133 @@ 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 = new ServerSocket(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(); + } + } + } + /* 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. */ From f19c9e1703a58f4bd72fbcfed5e2ac60801af374 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 22 Oct 2026 10:12:46 -0600 Subject: [PATCH 2/5] JNI/JSSE: Stop select() waits on Windows when closing and enable close interrupt tests --- native/com_wolfssl_WolfSSLSession.c | 76 ++++++++-- src/java/com/wolfssl/WolfSSLSession.java | 6 + .../wolfssl/provider/jsse/WolfSSLSocket.java | 7 + .../provider/jsse/test/WolfSSLSocketTest.java | 88 ++++++++--- .../com/wolfssl/test/WolfSSLSessionTest.java | 138 ++++++++++++++++++ 5 files changed, 283 insertions(+), 32 deletions(-) diff --git a/native/com_wolfssl_WolfSSLSession.c b/native/com_wolfssl_WolfSSLSession.c index 4e2660e9..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] */ @@ -907,6 +913,12 @@ static int socketSelect(SSLAppData* appData, int sockfd, int timeout_ms, int rx, 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 by default). */ @@ -1005,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; @@ -1291,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); @@ -1528,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); @@ -6920,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; } @@ -6967,26 +7009,42 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_restoreFdBlocking { #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. */ + * 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)) { - return SSL_FAILURE; + ret = SSL_FAILURE; } + wc_UnLockMutex(appData->jniSessLock); + + return ret; #else (void)sslPtr; -#endif return SSL_SUCCESS; +#endif /* USE_WINDOWS_API */ } JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLSession_setSSLIORecv diff --git a/src/java/com/wolfssl/WolfSSLSession.java b/src/java/com/wolfssl/WolfSSLSession.java index bc1a63c5..7a41edd4 100644 --- a/src/java/com/wolfssl/WolfSSLSession.java +++ b/src/java/com/wolfssl/WolfSSLSession.java @@ -5728,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. * diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java b/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java index 4adea6fb..e7ee254e 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java @@ -3223,6 +3223,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/WolfSSLSocketTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java index 5d4de5f5..b23bf9af 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java @@ -3914,11 +3914,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()) { @@ -3993,11 +3988,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 poll") && + !msg.contains("Socket is closed"))) { e.printStackTrace(); fail("Incorrect SocketException thrown by client"); throw e; @@ -4032,11 +4031,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()) { @@ -4332,6 +4326,64 @@ public Void call() throws Exception { } } + /* 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 = (SSLServerSocket)ctx.getServerSocketFactory() + .createServerSocket(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 resource, ignoring exceptions, for deterministic * per-iteration cleanup in the stress tests below. */ private static void closeQuietly(java.io.Closeable c) { @@ -4392,11 +4444,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()) { @@ -4575,11 +4622,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()) { diff --git a/src/test/com/wolfssl/test/WolfSSLSessionTest.java b/src/test/com/wolfssl/test/WolfSSLSessionTest.java index 57938396..ff329645 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; @@ -6865,6 +6866,143 @@ public Void call() throws Exception { } } + /** + * 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. */ From bcf8bb720047cac7f27f5bf5193d0f8509115b5f Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 22 Oct 2026 10:12:47 -0600 Subject: [PATCH 3/5] JNI/JSSE: Name select/poll() in socket error messages, use WSAGetLastError() on Windows --- native/com_wolfssl_WolfSSL.c | 2 +- src/java/com/wolfssl/WolfSSL.java | 15 ++++++++++----- src/java/com/wolfssl/WolfSSLSession.java | 8 ++++---- .../com/wolfssl/provider/jsse/WolfSSLSocket.java | 8 ++++---- .../provider/jsse/test/WolfSSLSocketTest.java | 2 +- 5 files changed, 20 insertions(+), 15 deletions(-) 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/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 7a41edd4..ee4d5fd3 100644 --- a/src/java/com/wolfssl/WolfSSLSession.java +++ b/src/java/com/wolfssl/WolfSSLSession.java @@ -1042,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(), " + diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java b/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java index e7ee254e..71bd8dec 100644 --- a/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java +++ b/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java @@ -3024,12 +3024,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; } diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java index b23bf9af..011d3915 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java @@ -3995,7 +3995,7 @@ public Void call() throws Exception { * 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"); From a93757313cb194d27eedff18e2f7b9758ff9b267 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 22 Oct 2026 10:12:47 -0600 Subject: [PATCH 4/5] JNI/JSSE: Make test setup re-runnable and avoid test listener port collisions --- .../jsse/test/WolfSSLContextTest.java | 4 + .../provider/jsse/test/WolfSSLEngineTest.java | 7 +- .../jsse/test/WolfSSLParametersPskTest.java | 6 +- .../test/WolfSSLServerSocketFactoryTest.java | 5 + .../jsse/test/WolfSSLServerSocketTest.java | 44 ++- .../jsse/test/WolfSSLSessionTest.java | 11 +- .../jsse/test/WolfSSLSocketFactoryTest.java | 5 + .../provider/jsse/test/WolfSSLSocketTest.java | 261 +++++++++--------- .../jsse/test/WolfSSLTestFactory.java | 76 +++++ .../jsse/test/WolfSSLTrustX509Test.java | 50 ++-- .../com/wolfssl/test/WolfSSLContextTest.java | 8 +- .../com/wolfssl/test/WolfSSLSessionTest.java | 45 +-- .../com/wolfssl/test/WolfSSLTestCommon.java | 27 ++ 13 files changed, 328 insertions(+), 221 deletions(-) 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 011d3915..e1cb10fa 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.*; @@ -202,6 +203,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 +274,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 +378,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 +413,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 +423,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 +521,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 +633,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 +833,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 +882,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 +931,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 +982,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 +1032,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 +1084,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 +1281,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 +1362,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 +1437,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 +1579,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 +1664,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 +1693,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 +1756,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 +1808,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 +1907,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 +2032,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 +2085,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 +2430,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 +2474,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 +2526,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 +2589,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 +2638,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 +2762,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 +2880,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 +2945,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 +2956,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 +3012,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 +3122,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 +3260,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 +3431,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 +3533,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 +3615,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 +3727,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(); @@ -3927,11 +3936,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); @@ -4044,11 +4053,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); @@ -4163,8 +4172,7 @@ private void socketReadTimeoutTest(boolean layered) throws Exception { this.ctx = tf.createSSLContext("TLS", ctxProvider); try { - ss = (SSLServerSocket)ctx.getServerSocketFactory() - .createServerSocket(0); + ss = WolfSSLTestFactory.createServerSocket(ctx, 0); InetSocketAddress addr = new InetSocketAddress( InetAddress.getLoopbackAddress(), ss.getLocalPort()); @@ -4347,8 +4355,7 @@ public void testSocketCloseTls13ClientDoesNotWaitForTicket() this.ctx = tf.createSSLContext("TLSv1.3", ctxProvider); try { - ss = (SSLServerSocket)ctx.getServerSocketFactory() - .createServerSocket(0); + ss = WolfSSLTestFactory.createServerSocket(ctx, 0); cs = (SSLSocket)ctx.getSocketFactory().createSocket(); cs.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), ss.getLocalPort())); @@ -4490,8 +4497,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() */ @@ -4671,8 +4677,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() */ @@ -4826,8 +4831,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())); @@ -4971,7 +4975,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 @@ -5061,7 +5065,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())); @@ -5134,8 +5138,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())); @@ -5292,7 +5295,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())); @@ -5379,11 +5382,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(); @@ -5571,8 +5574,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 @@ -5616,8 +5618,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); @@ -5670,8 +5671,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 = @@ -5689,7 +5690,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"); @@ -5725,7 +5727,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"); @@ -5787,8 +5790,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 = @@ -5805,7 +5808,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. */ @@ -6120,7 +6124,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(); @@ -6432,8 +6437,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; @@ -6631,8 +6635,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 ff329645..9b1223fd 100644 --- a/src/test/com/wolfssl/test/WolfSSLSessionTest.java +++ b/src/test/com/wolfssl/test/WolfSSLSessionTest.java @@ -616,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, @@ -830,7 +830,7 @@ private void assertHandshakeFailsWithVerifyCallback( new WolfSSL(); try { - srvSocket = new ServerSocket(0); + srvSocket = WolfSSLTestCommon.createServerSocket(0); srvSocket.setSoTimeout(10000); int port = srvSocket.getLocalPort(); @@ -1017,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; @@ -1136,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); @@ -1571,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(); @@ -1966,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(); @@ -2126,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(); @@ -2288,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(); @@ -2659,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, @@ -2928,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; @@ -3241,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, @@ -3555,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()); @@ -3860,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, @@ -4067,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, @@ -4245,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()); @@ -4402,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()); @@ -4558,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, @@ -6111,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()); @@ -6482,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, @@ -6655,7 +6656,7 @@ public void test_WolfSSLSession_shutdownBeforeHandshakeDone() final CountDownLatch clientDone = new CountDownLatch(1); try { - srvSocket = new ServerSocket(0); + srvSocket = WolfSSLTestCommon.createServerSocket(0); srvSocket.setSoTimeout(10000); final ServerSocket srv = srvSocket; @@ -6767,7 +6768,7 @@ public void test_WolfSSLSession_readTimeout() throws Exception { int ret; try { - srvSocket = new ServerSocket(0); + srvSocket = WolfSSLTestCommon.createServerSocket(0); srvSocket.setSoTimeout(10000); final ServerSocket srv = srvSocket; @@ -7166,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; + } } From bf7cdea43648eb98fe214fbce1fdccef7bac0c72 Mon Sep 17 00:00:00 2001 From: Chris Conlon Date: Thu, 22 Oct 2026 10:12:47 -0600 Subject: [PATCH 5/5] JNI/JSSE: Treat SO_LINGER as seconds in close() and do not wait when linger is 0 --- .../wolfssl/provider/jsse/WolfSSLSocket.java | 21 ++-- .../provider/jsse/test/WolfSSLSocketTest.java | 102 +++++++++++++++++- 2 files changed, 114 insertions(+), 9 deletions(-) diff --git a/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java b/src/java/com/wolfssl/provider/jsse/WolfSSLSocket.java index 71bd8dec..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); diff --git a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java index e1cb10fa..fa60fe64 100644 --- a/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java +++ b/src/test/com/wolfssl/provider/jsse/test/WolfSSLSocketTest.java @@ -47,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; @@ -3962,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) { @@ -4078,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) { @@ -4391,6 +4394,101 @@ public Void call() throws Exception { } } + /* 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) {