Merge pull request #246 from cconlon/socketCloseInterruptsWriteRead

JSSE: calling SSLSocket.close() should interrupt threads blocked in select()/poll()
pull/249/head
JacobBarthelmeh 2025-01-21 15:29:17 -08:00 committed by GitHub
commit d56fa67109
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 971 additions and 108 deletions

View File

@ -15,6 +15,9 @@ on:
wolfssl_configure:
required: true
type: string
javash_cflags:
required: false
type: string
jobs:
build_wolfssljni:
@ -51,7 +54,7 @@ jobs:
echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$GITHUB_WORKSPACE/build-dir/lib" >> "$GITHUB_ENV"
- name: Build JNI library
run: ./java.sh $GITHUB_WORKSPACE/build-dir
run: CFLAGS=${{ inputs.javah_cflags }} ./java.sh $GITHUB_WORKSPACE/build-dir
- name: Build JAR (ant)
run: ant
- name: Run Java tests (ant test)

View File

@ -120,6 +120,27 @@ jobs:
jdk_version: ${{ matrix.jdk_version }}
wolfssl_configure: ${{ matrix.wolfssl_configure }}
# -------------------- WOLFJNI_USE_IO_SELECT sanity check --------------------
# Only check one Linux and Mac JDK version as a sanity check.
# Using Zulu, but this can be expanded if needed.
linux-zulu-ioselect:
strategy:
matrix:
os: [ 'ubuntu-latest', 'macos-latest' ]
jdk_version: [ '11' ]
wolfssl_configure: [
'--enable-jni',
]
javash_cflags: [ '-DWOLFJNI_USE_IO_SELECT' ]
name: ${{ matrix.os }} (Zulu JDK ${{ matrix.jdk_version }}, ${{ matrix.wolfssl_configure}}, ${{ matrix.javash_cflags }})
uses: ./.github/workflows/linux-common.yml
with:
os: ${{ matrix.os }}
jdk_distro: "zulu"
jdk_version: ${{ matrix.jdk_version }}
wolfssl_configure: ${{ matrix.wolfssl_configure }}
javash_cflags: ${{ matrix.javash_cflags }}
# ------------------ Facebook Infer static analysis -------------------
# Run Facebook infer over PR code, only running on Linux with one
# JDK/version for now.

View File

@ -108,6 +108,44 @@ $ ./examples/provider/ServerJSSE.sh
$ ./examples/provider/ClientJSSE.sh
```
### java.sh Script Options
The `java.sh` script compiles the native JNI sources into a shared library named
either `libwolfssljni.so` (Linux/Unix) or `libwolfssljni.dylib` (MacOS).
Compiling on Linux/Unix and Mac OSX are currently supported.
This script will attempt to auto-detect the `JAVA_HOME` location if not set.
To explicitly use a Java home location, set the `JAVA_HOME` environment variable
prior to running this script.
This script will try to link against a wolfSSL library installed to the
default location of `/usr/local`. This script accepts two arguments on the
command line. The first argument can point to a custom wolfSSL installation
location. A custom install location would match the directory set at wolfSSL
`./configure --prefix=<DIR>`.
The second argument represents the wolfSSL library name that should be
linked against. This is helpful if a non-standard library name has been
used with wolfSSL, for example the `./configure --with-libsuffix` option
has been used to add a suffix to the wolfSSL library name. Note that to
use this argument, an installation location must be specified via the
first argument.
For example, if wolfSSL was configured with `--with-libsuffix=jsse`, then
this script could be called like so using the default installation
path of `/usr/local`:
```
java.sh /usr/local wolfssljsse
```
`java.sh` can use preset `CFLAGS` defines, if set in the environment variable
prior to running the script, for example:
```
CFLAGS=-DWOLFJNI_USE_IO_SELECT java.sh
```
## Building with Maven
wolfJSSE supports building and packaging with Maven, for those projects that

26
java.sh
View File

@ -75,7 +75,6 @@ if [ "$OS" == "Darwin" ] ; then
javaIncludes="-I$javaHome/include -I$javaHome/include/darwin -I$WOLFSSL_INSTALL_DIR/include"
javaLibs="-dynamiclib"
jniLibName="libwolfssljni.dylib"
cflags=""
elif [ "$OS" == "Linux" ] ; then
echo " Detected Linux host OS"
if [ -z $javaHome ]; then
@ -88,7 +87,6 @@ elif [ "$OS" == "Linux" ] ; then
javaIncludes="-I$javaHome/include -I$javaHome/include/linux -I$WOLFSSL_INSTALL_DIR/include"
javaLibs="-shared"
jniLibName="libwolfssljni.so"
cflags=""
if [ "$ARCH" == "x86_64" ] ; then
fpic="-fPIC"
else
@ -108,18 +106,18 @@ then
mkdir ./lib
fi
gcc -Wall -c $fpic $cflags ./native/com_wolfssl_WolfSSL.c -o ./native/com_wolfssl_WolfSSL.o $javaIncludes
gcc -Wall -c $fpic $cflags ./native/com_wolfssl_WolfSSLSession.c -o ./native/com_wolfssl_WolfSSLSession.o $javaIncludes
gcc -Wall -c $fpic $cflags ./native/com_wolfssl_WolfSSLContext.c -o ./native/com_wolfssl_WolfSSLContext.o $javaIncludes
gcc -Wall -c $fpic $cflags ./native/com_wolfssl_wolfcrypt_RSA.c -o ./native/com_wolfssl_wolfcrypt_RSA.o $javaIncludes
gcc -Wall -c $fpic $cflags ./native/com_wolfssl_wolfcrypt_ECC.c -o ./native/com_wolfssl_wolfcrypt_ECC.o $javaIncludes
gcc -Wall -c $fpic $cflags ./native/com_wolfssl_wolfcrypt_EccKey.c -o ./native/com_wolfssl_wolfcrypt_EccKey.o $javaIncludes
gcc -Wall -c $fpic $cflags ./native/com_wolfssl_WolfSSLCertManager.c -o ./native/com_wolfssl_WolfSSLCertManager.o $javaIncludes
gcc -Wall -c $fpic $cflags ./native/com_wolfssl_WolfSSLCertRequest.c -o ./native/com_wolfssl_WolfSSLCertRequest.o $javaIncludes
gcc -Wall -c $fpic $cflags ./native/com_wolfssl_WolfSSLCertificate.c -o ./native/com_wolfssl_WolfSSLCertificate.o $javaIncludes
gcc -Wall -c $fpic $cflags ./native/com_wolfssl_WolfSSLX509Name.c -o ./native/com_wolfssl_WolfSSLX509Name.o $javaIncludes
gcc -Wall -c $fpic $cflags ./native/com_wolfssl_WolfSSLX509StoreCtx.c -o ./native/com_wolfssl_WolfSSLX509StoreCtx.o $javaIncludes
gcc -Wall $javaLibs $cflags -o ./lib/$jniLibName ./native/com_wolfssl_WolfSSL.o ./native/com_wolfssl_WolfSSLSession.o ./native/com_wolfssl_WolfSSLContext.o ./native/com_wolfssl_wolfcrypt_RSA.o ./native/com_wolfssl_wolfcrypt_ECC.o ./native/com_wolfssl_wolfcrypt_EccKey.o ./native/com_wolfssl_WolfSSLCertManager.o ./native/com_wolfssl_WolfSSLCertRequest.o ./native/com_wolfssl_WolfSSLCertificate.o ./native/com_wolfssl_WolfSSLX509Name.o ./native/com_wolfssl_WolfSSLX509StoreCtx.o -L$WOLFSSL_INSTALL_DIR/lib -L$WOLFSSL_INSTALL_DIR/lib64 -l$WOLFSSL_LIBNAME
gcc -Wall -c $fpic $CFLAGS ./native/com_wolfssl_WolfSSL.c -o ./native/com_wolfssl_WolfSSL.o $javaIncludes
gcc -Wall -c $fpic $CFLAGS ./native/com_wolfssl_WolfSSLSession.c -o ./native/com_wolfssl_WolfSSLSession.o $javaIncludes
gcc -Wall -c $fpic $CFLAGS ./native/com_wolfssl_WolfSSLContext.c -o ./native/com_wolfssl_WolfSSLContext.o $javaIncludes
gcc -Wall -c $fpic $CFLAGS ./native/com_wolfssl_wolfcrypt_RSA.c -o ./native/com_wolfssl_wolfcrypt_RSA.o $javaIncludes
gcc -Wall -c $fpic $CFLAGS ./native/com_wolfssl_wolfcrypt_ECC.c -o ./native/com_wolfssl_wolfcrypt_ECC.o $javaIncludes
gcc -Wall -c $fpic $CFLAGS ./native/com_wolfssl_wolfcrypt_EccKey.c -o ./native/com_wolfssl_wolfcrypt_EccKey.o $javaIncludes
gcc -Wall -c $fpic $CFLAGS ./native/com_wolfssl_WolfSSLCertManager.c -o ./native/com_wolfssl_WolfSSLCertManager.o $javaIncludes
gcc -Wall -c $fpic $CFLAGS ./native/com_wolfssl_WolfSSLCertRequest.c -o ./native/com_wolfssl_WolfSSLCertRequest.o $javaIncludes
gcc -Wall -c $fpic $CFLAGS ./native/com_wolfssl_WolfSSLCertificate.c -o ./native/com_wolfssl_WolfSSLCertificate.o $javaIncludes
gcc -Wall -c $fpic $CFLAGS ./native/com_wolfssl_WolfSSLX509Name.c -o ./native/com_wolfssl_WolfSSLX509Name.o $javaIncludes
gcc -Wall -c $fpic $CFLAGS ./native/com_wolfssl_WolfSSLX509StoreCtx.c -o ./native/com_wolfssl_WolfSSLX509StoreCtx.o $javaIncludes
gcc -Wall $javaLibs $CFLAGS -o ./lib/$jniLibName ./native/com_wolfssl_WolfSSL.o ./native/com_wolfssl_WolfSSLSession.o ./native/com_wolfssl_WolfSSLContext.o ./native/com_wolfssl_wolfcrypt_RSA.o ./native/com_wolfssl_wolfcrypt_ECC.o ./native/com_wolfssl_wolfcrypt_EccKey.o ./native/com_wolfssl_WolfSSLCertManager.o ./native/com_wolfssl_WolfSSLCertRequest.o ./native/com_wolfssl_WolfSSLCertificate.o ./native/com_wolfssl_WolfSSLX509Name.o ./native/com_wolfssl_WolfSSLX509StoreCtx.o -L$WOLFSSL_INSTALL_DIR/lib -L$WOLFSSL_INSTALL_DIR/lib64 -l$WOLFSSL_LIBNAME
if [ $? != 0 ]; then
echo "Error creating native JNI library"
exit 1

View File

@ -72,8 +72,17 @@ static jobject g_crlCbIfaceObj;
* function calls. Stored inside WOLFSSL app data, set with
* wolfSSL_set_app_data(), retrieved with wolfSSL_get_app_data().
* Global callback objects are created with NewGlobalRef(), then freed
* inside freeSSL() with DeleteGlobalRef(). */
* inside freeSSL() with DeleteGlobalRef().
*
* 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. */
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 */
#endif
wolfSSL_Mutex* jniSessLock; /* WOLFSSL session lock */
jobject* g_verifySSLCbIfaceObj; /* Java verify callback [global ref] */
} SSLAppData;
@ -191,13 +200,116 @@ int NativeSSLVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store)
return retval;
}
#ifndef USE_WINDOWS_API
/* Close interrupt pipe() descriptors and reset back to -1. */
static void closeInterruptPipe(SSLAppData* appData)
{
if (appData != NULL) {
if (appData->interruptFds[0] != -1) {
close(appData->interruptFds[0]);
appData->interruptFds[0] = -1;
}
if (appData->interruptFds[1] != -1) {
close(appData->interruptFds[1]);
appData->interruptFds[1] = -1;
}
}
}
/* Signal to threads blocked in select() or poll() to wake up, by writing
* one byte to the appData.interruptFds[1] pipe. */
static void writeToInterruptPipe(SSLAppData* appData)
{
if (appData != NULL) {
if (appData->interruptFds[1] != -1) {
write(appData->interruptFds[1], "1", 1);
}
}
}
#endif /* !USE_WINDOWS_API */
/* Return number of threads waiting in poll()/select() */
static int threadsWaitingInPollSelect(SSLAppData* appData)
{
int ret = 0;
#ifndef USE_WINDOWS_API
wolfSSL_Mutex* pollCountLock = NULL;
if (appData != NULL) {
pollCountLock = appData->pollCountLock;
if (pollCountLock != NULL) {
if (wc_LockMutex(pollCountLock) == 0) {
ret = appData->threadsInPoll;
wc_UnLockMutex(pollCountLock);
}
}
}
#endif
return ret;
}
static void incrementThreadPollCount(SSLAppData* appData)
{
#ifndef USE_WINDOWS_API
wolfSSL_Mutex* pollCountLock = NULL;
if (appData == NULL) {
return;
}
pollCountLock = appData->pollCountLock;
if (pollCountLock == NULL) {
return;
}
if (wc_LockMutex(pollCountLock) != 0) {
return;
}
appData->threadsInPoll++;
wc_UnLockMutex(pollCountLock);
#endif
}
static void decrementThreadPollCount(SSLAppData* appData)
{
#ifndef USE_WINDOWS_API
wolfSSL_Mutex* pollCountLock = NULL;
if (appData == NULL) {
return;
}
pollCountLock = appData->pollCountLock;
if (pollCountLock == NULL) {
return;
}
if (wc_LockMutex(pollCountLock) != 0) {
return;
}
if (appData->threadsInPoll > 0) {
appData->threadsInPoll--;
}
wc_UnLockMutex(pollCountLock);
#endif
}
JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_newSSL
(JNIEnv* jenv, jobject jcl, jlong ctx)
(JNIEnv* jenv, jobject jcl, jlong ctx, jboolean withIOPipe)
{
int ret;
jlong sslPtr = 0;
jobject* g_cachedSSLObj = NULL;
wolfSSL_Mutex* jniSessLock = NULL;
#ifndef USE_WINDOWS_API
wolfSSL_Mutex* pollCountLock = NULL;
#endif
SSLAppData* appData = NULL;
if (jenv == NULL) {
@ -251,11 +363,71 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_newSSL
wc_InitMutex(jniSessLock);
appData->jniSessLock = jniSessLock;
/* set up interrupt pipe for SSLSocket.close() to use if/when needed.
* currently only non-Windows platforms supported due to Windows not
* supporting direct/same pipe() operation. Make read pipe non
* blocking since byte read from it could have already been taken
* out by either reader/writer thread before the other has a chance
* to read it. But, we only use it for waking us up and don't care
* much about actually reading the byte passed over the pipe. */
appData->threadsInPoll = 0;
#ifndef USE_WINDOWS_API
pollCountLock = (wolfSSL_Mutex*)XMALLOC(sizeof(wolfSSL_Mutex), NULL,
DYNAMIC_TYPE_TMP_BUFFER);
if (pollCountLock == NULL) {
printf("error mallocing pollCountLock in newSSL for SSLAppData\n");
(*jenv)->DeleteGlobalRef(jenv, *g_cachedSSLObj);
XFREE(jniSessLock, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(appData, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(g_cachedSSLObj, NULL, DYNAMIC_TYPE_TMP_BUFFER);
wolfSSL_free((WOLFSSL*)(uintptr_t)sslPtr);
return SSL_FAILURE;
}
wc_InitMutex(pollCountLock);
appData->pollCountLock = pollCountLock;
appData->interruptFds[0] = -1;
appData->interruptFds[1] = -1;
if (withIOPipe == JNI_TRUE) {
ret = pipe(appData->interruptFds);
if (ret == -1) {
printf("error setting up pipe() for interruptFds[] in newSSL\n");
(*jenv)->DeleteGlobalRef(jenv, *g_cachedSSLObj);
XFREE(pollCountLock, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(jniSessLock, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(appData, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(g_cachedSSLObj, NULL, DYNAMIC_TYPE_TMP_BUFFER);
wolfSSL_free((WOLFSSL*)(uintptr_t)sslPtr);
return SSL_FAILURE;
}
ret = fcntl(appData->interruptFds[0], F_SETFL,
fcntl(appData->interruptFds[0], F_GETFL, 0) | O_NONBLOCK);
if (ret < 0) {
printf("error setting interruptFds[0] non-blocking in newSSL\n");
closeInterruptPipe(appData);
(*jenv)->DeleteGlobalRef(jenv, *g_cachedSSLObj);
XFREE(pollCountLock, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(jniSessLock, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(appData, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(g_cachedSSLObj, NULL, DYNAMIC_TYPE_TMP_BUFFER);
wolfSSL_free((WOLFSSL*)(uintptr_t)sslPtr);
return SSL_FAILURE;
}
}
#endif /* !USE_WINDOWS_API */
/* cache associated WolfSSLSession jobject in native WOLFSSL */
ret = wolfSSL_set_jobject((WOLFSSL*)(uintptr_t)sslPtr, g_cachedSSLObj);
if (ret != SSL_SUCCESS) {
printf("error storing jobject in wolfSSL native session\n");
(*jenv)->DeleteGlobalRef(jenv, *g_cachedSSLObj);
#ifndef USE_WINDOWS_API
closeInterruptPipe(appData);
XFREE(pollCountLock, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
XFREE(jniSessLock, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(appData, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(g_cachedSSLObj, NULL, DYNAMIC_TYPE_TMP_BUFFER);
wolfSSL_free((WOLFSSL*)(uintptr_t)sslPtr);
@ -267,6 +439,10 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_newSSL
(WOLFSSL*)(uintptr_t)sslPtr, appData) != SSL_SUCCESS) {
printf("error setting WOLFSSL app data in newSSL\n");
(*jenv)->DeleteGlobalRef(jenv, *g_cachedSSLObj);
#ifndef USE_WINDOWS_API
closeInterruptPipe(appData);
XFREE(pollCountLock, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
XFREE(jniSessLock, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(appData, NULL, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(g_cachedSSLObj, NULL, DYNAMIC_TYPE_TMP_BUFFER);
@ -637,16 +813,21 @@ enum {
* supported, since not supported on Java Socket.
* @param rx set to 1 to monitor readability on socket descriptor,
* otherwise 0 to monitor writability
* @param shutdown Is this being called from shutdownSSL()? Don't select
* on interruptFds in that case, since we already know
* we are closing in that case.
*
* @return possible return values are:
* WOLFJNI_IO_EVENT_FAIL
* WOLFJNI_IO_EVENT_ERROR
* WOLFJNI_IO_EVENT_FD_CLOSED
* WOLFJNI_IO_EVENT_TIMEOUT
* WOLFJNI_IO_EVENT_RECV_READY
* WOLFJNI_IO_EVENT_SEND_READY
* WOLFJNI_IO_EVENT_INVALID_TIMEOUT
*/
static int socketSelect(int sockfd, int timeout_ms, int rx)
static int socketSelect(SSLAppData* appData, int sockfd, int timeout_ms, int rx,
int shutdown)
{
fd_set fds, errfds;
fd_set* recvfds = NULL;
@ -654,20 +835,39 @@ static int socketSelect(int sockfd, int timeout_ms, int rx)
int nfds = sockfd + 1;
int result = 0;
struct timeval timeout;
#ifndef USE_WINDOWS_API
char tmpBuf[1];
#endif
/* Java Socket does not support negative timeouts, sanitize */
if (timeout_ms < 0) {
return WOLFJNI_IO_EVENT_INVALID_TIMEOUT;
}
if (appData == NULL) {
return WOLFJNI_IO_EVENT_ERROR;
}
#ifndef USE_WINDOWS_API
do {
#endif
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_usec = (timeout_ms % 1000) * 1000;
/* file/socket descriptors */
FD_ZERO(&fds);
FD_SET(sockfd, &fds);
#ifndef USE_WINDOWS_API
if ((shutdown == 0) && (appData->interruptFds[0] != -1)) {
FD_SET(appData->interruptFds[0], &fds);
/* nfds should be set to the highest number descriptor plus 1 */
if (appData->interruptFds[0] > sockfd) {
nfds = appData->interruptFds[0] + 1;
}
}
#endif /* !USE_WINDOWS_API */
/* error descriptors */
FD_ZERO(&errfds);
FD_SET(sockfd, &errfds);
@ -677,11 +877,13 @@ static int socketSelect(int sockfd, int timeout_ms, int rx)
sendfds = &fds;
}
incrementThreadPollCount(appData);
if (timeout_ms == 0) {
result = select(nfds, recvfds, sendfds, &errfds, NULL);
} else {
result = select(nfds, recvfds, sendfds, &errfds, &timeout);
}
decrementThreadPollCount(appData);
if (result == 0) {
return WOLFJNI_IO_EVENT_TIMEOUT;
@ -692,9 +894,26 @@ static int socketSelect(int sockfd, int timeout_ms, int rx)
} else {
return WOLFJNI_IO_EVENT_SEND_READY;
}
} else if (FD_ISSET(sockfd, &errfds)) {
}
else if (FD_ISSET(sockfd, &errfds)) {
return WOLFJNI_IO_EVENT_ERROR;
}
#ifndef USE_WINDOWS_API
else if ((shutdown == 0) && (appData->interruptFds[0] != -1) &&
(FD_ISSET(appData->interruptFds[0], &fds))) {
/* We got interrupted by our interrupt fd, due to a Java
* thread calling SSLSocket.close(). Try to read byte that
* was placed on our interruptFds[0] descriptor, but not
* an error if not there. Another read/write() may have
* already read it off. We just want to be interrupted,
* byte value does not matter. */
do {
read(appData->interruptFds[0], tmpBuf, 1);
} while (errno == EINTR);
return WOLFJNI_IO_EVENT_FD_CLOSED;
}
#endif /* !USE_WINDOWS_API */
}
#ifndef USE_WINDOWS_API
@ -725,6 +944,9 @@ static int socketSelect(int sockfd, int timeout_ms, int rx)
* otherwise 0 to ignore readability events
* @param tx set to 1 to monitor writability on socket descriptor,
* otherwise 0 to ignore writability events
* @param shutdown Is this being called from shutdownSSL()? Don't poll
* on interruptFds in that case, since we already know
* we are closing in that case.
*
* @return possible return values are:
* WOLFJNI_IO_EVENT_FAIL
@ -736,11 +958,18 @@ static int socketSelect(int sockfd, int timeout_ms, int rx)
* WOLFJNI_IO_EVENT_POLLHUP
* WOLFJNI_IO_EVENT_INVALID_TIMEOUT
*/
static int socketPoll(int sockfd, int timeout_ms, int rx, int tx)
static int socketPoll(SSLAppData* appData, int sockfd, int timeout_ms,
int rx, int tx, int shutdown)
{
int ret;
int nfds = 2;
int timeout;
struct pollfd fds[1];
struct pollfd fds[2];
char tmpBuf[1];
if (appData == NULL) {
return WOLFJNI_IO_EVENT_ERROR;
}
/* Sanitize timeout and convert from Java to poll() expectations */
timeout = timeout_ms;
@ -750,35 +979,62 @@ static int socketPoll(int sockfd, int timeout_ms, int rx, int tx)
timeout = -1;
}
/* fd for socket I/O */
fds[0].fd = sockfd;
fds[0].events = 0;
if (tx) {
fds[0].events |= POLLOUT;
fds[0].events |= POLLOUT | POLLPRI;
}
if (rx) {
fds[0].events |= POLLIN;
fds[0].events |= POLLIN | POLLPRI;
}
if ((shutdown == 0) && (appData->interruptFds[0] != -1)) {
/* fd for interrupt / signaling SSLSocket.close() */
fds[1].fd = appData->interruptFds[0];
fds[1].events = POLLIN | POLLPRI;
}
else {
nfds--;
}
do {
ret = poll(fds, 1, timeout);
incrementThreadPollCount(appData);
ret = poll(fds, nfds, timeout);
decrementThreadPollCount(appData);
if (ret == 0) {
return WOLFJNI_IO_EVENT_TIMEOUT;
} else if (ret > 0) {
if (fds[0].revents & POLLIN ||
}
else if (ret > 0) {
if ((shutdown == 0) && (appData->interruptFds[0] != -1) &&
(fds[1].revents & POLLIN)) {
/* received data on interrupt pipe, read and return
* that descriptor is closed (closing) */
do {
read(appData->interruptFds[0], tmpBuf, 1);
} while (errno == EINTR);
return WOLFJNI_IO_EVENT_FD_CLOSED;
}
else if (fds[0].revents & POLLIN ||
fds[0].revents & POLLPRI) { /* read possible */
return WOLFJNI_IO_EVENT_RECV_READY;
} else if (fds[0].revents & POLLOUT) { /* write possible */
}
else if (fds[0].revents & POLLOUT) { /* write possible */
return WOLFJNI_IO_EVENT_SEND_READY;
} else if (fds[0].revents & POLLNVAL) { /* fd not open */
}
else if (fds[0].revents & POLLNVAL) { /* fd not open */
return WOLFJNI_IO_EVENT_FD_CLOSED;
} else if (fds[0].revents & POLLERR) { /* exceptional error */
}
else if (fds[0].revents & POLLERR) { /* exceptional error */
return WOLFJNI_IO_EVENT_ERROR;
} else if (fds[0].revents & POLLHUP) { /* sock disconnected */
}
else if (fds[0].revents & POLLHUP) { /* sock disconnected */
return WOLFJNI_IO_EVENT_POLLHUP;
}
}
@ -795,7 +1051,9 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_connect
{
int ret = 0, err = 0, sockfd = 0;
int pollRx = 0;
#if !defined(WOLFJNI_USE_IO_SELECT) && !defined(USE_WINDOWS_API)
int pollTx = 0;
#endif
wolfSSL_Mutex* jniSessLock = NULL;
SSLAppData* appData = NULL;
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
@ -852,14 +1110,16 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_connect
if (err == SSL_ERROR_WANT_READ) {
pollRx = 1;
}
#if !defined(WOLFJNI_USE_IO_SELECT) && !defined(USE_WINDOWS_API)
else if (err == SSL_ERROR_WANT_WRITE) {
pollTx = 1;
}
#endif
#if defined(WOLFJNI_USE_IO_SELECT) || defined(USE_WINDOWS_API)
ret = socketSelect(sockfd, (int)timeout, pollRx);
ret = socketSelect(appData, sockfd, (int)timeout, pollRx, 0);
#else
ret = socketPoll(sockfd, (int)timeout, pollRx, pollTx);
ret = socketPoll(appData, sockfd, (int)timeout, pollRx, pollTx, 0);
#endif
if ((ret == WOLFJNI_IO_EVENT_RECV_READY) ||
(ret == WOLFJNI_IO_EVENT_SEND_READY)) {
@ -898,7 +1158,9 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_write
byte* data = NULL;
int ret = SSL_FAILURE, err, sockfd;
int pollRx = 0;
#if !defined(WOLFJNI_USE_IO_SELECT) && !defined(USE_WINDOWS_API)
int pollTx = 0;
#endif
wolfSSL_Mutex* jniSessLock = NULL;
SSLAppData* appData = NULL;
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
@ -963,14 +1225,17 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_write
if (err == SSL_ERROR_WANT_READ) {
pollRx = 1;
}
#if !defined(WOLFJNI_USE_IO_SELECT) && !defined(USE_WINDOWS_API)
else if (err == SSL_ERROR_WANT_WRITE) {
pollTx = 1;
}
#endif
#if defined(WOLFJNI_USE_IO_SELECT) || defined(USE_WINDOWS_API)
ret = socketSelect(sockfd, (int)timeout, pollRx);
ret = socketSelect(appData, sockfd, (int)timeout, pollRx, 0);
#else
ret = socketPoll(sockfd, (int)timeout, pollRx, pollTx);
ret = socketPoll(appData, sockfd, (int)timeout, pollRx,
pollTx, 0);
#endif
if ((ret == WOLFJNI_IO_EVENT_RECV_READY) ||
(ret == WOLFJNI_IO_EVENT_SEND_READY)) {
@ -1017,7 +1282,9 @@ static int SSLReadNonblockingWithSelectPoll(WOLFSSL* ssl, byte* out,
{
int size, ret, err, sockfd;
int pollRx = 0;
#if !defined(WOLFJNI_USE_IO_SELECT) && !defined(USE_WINDOWS_API)
int pollTx = 0;
#endif
wolfSSL_Mutex* jniSessLock = NULL;
SSLAppData* appData = NULL;
@ -1065,14 +1332,16 @@ static int SSLReadNonblockingWithSelectPoll(WOLFSSL* ssl, byte* out,
if (err == SSL_ERROR_WANT_READ) {
pollRx = 1;
}
#if !defined(WOLFJNI_USE_IO_SELECT) && !defined(USE_WINDOWS_API)
else if (err == SSL_ERROR_WANT_WRITE) {
pollTx = 1;
}
#endif
#if defined(WOLFJNI_USE_IO_SELECT) || defined(USE_WINDOWS_API)
ret = socketSelect(sockfd, timeout, pollRx);
ret = socketSelect(appData, sockfd, timeout, pollRx, 0);
#else
ret = socketPoll(sockfd, timeout, pollRx, pollTx);
ret = socketPoll(appData, sockfd, timeout, pollRx, pollTx, 0);
#endif
if ((ret == WOLFJNI_IO_EVENT_RECV_READY) ||
(ret == WOLFJNI_IO_EVENT_SEND_READY)) {
@ -1152,7 +1421,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_read__JLjava_nio_ByteBuff
jint position;
jint limit;
jboolean hasArray;
jbyteArray bufArr;
jbyteArray bufArr = NULL;
(void)jcl;
@ -1304,7 +1573,9 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_accept
{
int ret = 0, err, sockfd;
int pollRx = 0;
#if !defined(WOLFJNI_USE_IO_SELECT) && !defined(USE_WINDOWS_API)
int pollTx = 0;
#endif
wolfSSL_Mutex* jniSessLock = NULL;
SSLAppData* appData = NULL;
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
@ -1361,14 +1632,16 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_accept
if (err == SSL_ERROR_WANT_READ) {
pollRx = 1;
}
#if !defined(WOLFJNI_USE_IO_SELECT) && !defined(USE_WINDOWS_API)
else if (err == SSL_ERROR_WANT_WRITE) {
pollTx = 1;
}
#endif
#if defined(WOLFJNI_USE_IO_SELECT) || defined(USE_WINDOWS_API)
ret = socketSelect(sockfd, (int)timeout, pollRx);
ret = socketSelect(appData, sockfd, (int)timeout, pollRx, 0);
#else
ret = socketPoll(sockfd, (int)timeout, pollRx, pollTx);
ret = socketPoll(appData, sockfd, (int)timeout, pollRx, pollTx, 0);
#endif
if ((ret == WOLFJNI_IO_EVENT_RECV_READY) ||
(ret == WOLFJNI_IO_EVENT_SEND_READY)) {
@ -1441,6 +1714,16 @@ JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLSession_freeSSL
XFREE(g_cachedVerifyCb, NULL, DYNAMIC_TYPE_TMP_BUFFER);
g_cachedVerifyCb = NULL;
}
#ifndef USE_WINDOWS_API
if (appData->pollCountLock != NULL) {
wc_FreeMutex(appData->pollCountLock);
XFREE(appData->pollCountLock, NULL, DYNAMIC_TYPE_TMP_BUFFER);
appData->pollCountLock = NULL;
}
/* close pipe() descriptors */
closeInterruptPipe(appData);
#endif
/* free appData */
XFREE(appData, NULL, DYNAMIC_TYPE_TMP_BUFFER);
appData = NULL;
@ -1553,7 +1836,9 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_shutdownSSL
{
int ret = 0, err, sockfd;
int pollRx = 0;
#if !defined(WOLFJNI_USE_IO_SELECT) && !defined(USE_WINDOWS_API)
int pollTx = 0;
#endif
wolfSSL_Mutex* jniSessLock;
SSLAppData* appData = NULL;
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
@ -1610,14 +1895,16 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_shutdownSSL
if (err == SSL_ERROR_WANT_READ) {
pollRx = 1;
}
#if !defined(WOLFJNI_USE_IO_SELECT) && !defined(USE_WINDOWS_API)
else if (err == SSL_ERROR_WANT_WRITE) {
pollTx = 1;
}
#endif
#if defined(WOLFJNI_USE_IO_SELECT) || defined(USE_WINDOWS_API)
ret = socketSelect(sockfd, (int)timeout, pollRx);
ret = socketSelect(appData, sockfd, (int)timeout, pollRx, 1);
#else
ret = socketPoll(sockfd, (int)timeout, pollRx, pollTx);
ret = socketPoll(appData, sockfd, (int)timeout, pollRx, pollTx, 1);
#endif
if ((ret == WOLFJNI_IO_EVENT_RECV_READY) ||
(ret == WOLFJNI_IO_EVENT_SEND_READY)) {
@ -1822,11 +2109,11 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_get1Session
#if defined(WOLFJNI_USE_IO_SELECT) || defined(USE_WINDOWS_API)
/* Default to select() on Windows or if WOLFJNI_USE_IO_SELECT */
ret = socketSelect(sockfd,
(int)WOLFSSL_JNI_DEFAULT_PEEK_TIMEOUT, 1);
#else
ret = socketPoll(sockfd,
ret = socketSelect(appData, sockfd,
(int)WOLFSSL_JNI_DEFAULT_PEEK_TIMEOUT, 1, 0);
#else
ret = socketPoll(appData, sockfd,
(int)WOLFSSL_JNI_DEFAULT_PEEK_TIMEOUT, 1, 0, 0);
#endif
if ((ret == WOLFJNI_IO_EVENT_RECV_READY) ||
(ret == WOLFJNI_IO_EVENT_SEND_READY)) {
@ -3558,12 +3845,8 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_getSide
(void)jenv;
(void)jcl;
#ifdef ATOMIC_USER
/* wolfSSL checks ssl for NULL */
return wolfSSL_GetSide((WOLFSSL*)(uintptr_t)ssl);
#else
return NOT_COMPILED_IN;
#endif
}
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_isTLSv1_11
@ -5445,6 +5728,51 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_hasTicket
#endif
}
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;
}
/* 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 */
return SSL_SUCCESS;
}
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_getThreadsBlockedInPoll
(JNIEnv* jenv, jobject jcl, jlong sslPtr)
{
SSLAppData* appData = NULL;
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
(void)jenv;
(void)jcl;
/* get session mutex from SSL app data */
appData = (SSLAppData*)wolfSSL_get_app_data(ssl);
if (appData == NULL) {
return 0;
}
return (jint)threadsWaitingInPollSelect(appData);
}
JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLSession_setSSLIORecv
(JNIEnv* jenv, jobject jcl, jlong sslPtr)
{

View File

@ -10,10 +10,10 @@ extern "C" {
/*
* Class: com_wolfssl_WolfSSLSession
* Method: newSSL
* Signature: (J)J
* Signature: (JZ)J
*/
JNIEXPORT jlong JNICALL Java_com_wolfssl_WolfSSLSession_newSSL
(JNIEnv *, jobject, jlong);
(JNIEnv *, jobject, jlong, jboolean);
/*
* Class: com_wolfssl_WolfSSLSession
@ -879,6 +879,22 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_useSupportedCurve
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_hasTicket
(JNIEnv *, jobject, jlong);
/*
* Class: com_wolfssl_WolfSSLSession
* Method: interruptBlockedIO
* Signature: (J)I
*/
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_interruptBlockedIO
(JNIEnv *, jobject, jlong);
/*
* Class: com_wolfssl_WolfSSLSession
* Method: getThreadsBlockedInPoll
* Signature: (J)I
*/
JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_getThreadsBlockedInPoll
(JNIEnv *, jobject, jlong);
#ifdef __cplusplus
}
#endif

View File

@ -102,6 +102,13 @@ public class WolfSSLSession {
/**
* Creates a new SSL/TLS session.
*
* Native session created also creates JNI SSLAppData for usage
* internal to wolfSSL JNI. This constructor creates a default
* pipe() to use for interrupting threads waiting in select()/poll()
* when close() is called. To skip creation of this pipe() use
* the WolfSSLSession(WolfSSLContext ctx, boolean setupIOPipe)
* constructor with 'setupIOPipe' set to false.
*
* @param ctx WolfSSLContext object used to create SSL session.
*
* @throws com.wolfssl.WolfSSLException if session object creation
@ -109,13 +116,61 @@ public class WolfSSLSession {
*/
public WolfSSLSession(WolfSSLContext ctx) throws WolfSSLException {
sslPtr = newSSL(ctx.getContextPtr());
sslPtr = newSSL(ctx.getContextPtr(), true);
if (sslPtr == 0) {
throw new WolfSSLException("Failed to create SSL Object");
}
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
WolfSSLDebug.INFO, sslPtr, "creating new WolfSSLSession");
WolfSSLDebug.INFO, sslPtr,
"creating new WolfSSLSession (with I/O pipe)");
synchronized (stateLock) {
this.active = true;
}
/* save context reference for I/O callbacks from JNI */
this.ctx = ctx;
}
/**
* Creates a new SSL/TLS session.
*
* Native session created also creates JNI SSLAppData for usage
* internal to wolfSSL JNI. A pipe() can be created internally to wolfSSL
* JNI to use for interrupting threads waiting in select()/poll()
* when close() is called. To skip creation of this pipe(), set
* 'setupIOPipe' to false.
*
* It is generally recommended to have wolfSSL JNI create the native
* pipe(), unless you will be operating over non-Socket I/O. For example,
* when this WolfSSLSession is being created from the JSSE level
* SSLEngine class.
*
* @param ctx WolfSSLContext object used to create SSL session.
* @param setupIOPipe true to create internal IO pipe(), otherwise
* false
*
* @throws com.wolfssl.WolfSSLException if session object creation
* failed.
*/
public WolfSSLSession(WolfSSLContext ctx, boolean setupIOPipe)
throws WolfSSLException {
sslPtr = newSSL(ctx.getContextPtr(), false);
if (sslPtr == 0) {
throw new WolfSSLException("Failed to create SSL Object");
}
if (setupIOPipe) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
WolfSSLDebug.INFO, sslPtr,
"creating new WolfSSLSession (with I/O pipe)");
} else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
WolfSSLDebug.INFO, sslPtr,
"creating new WolfSSLSession (without I/O pipe)");
}
synchronized (stateLock) {
this.active = true;
@ -240,7 +295,7 @@ public class WolfSSLSession {
/* ------------------ native method declarations -------------------- */
private native long newSSL(long ctx);
private native long newSSL(long ctx, boolean withIOPipe);
private native int setFd(long ssl, Socket sd, int type);
private native int setFd(long ssl, DatagramSocket sd, int type);
private native int useCertificateFile(long ssl, String file, int format);
@ -357,6 +412,8 @@ public class WolfSSLSession {
private native int set1SigAlgsList(long ssl, String list);
private native int useSupportedCurve(long ssl, int name);
private native int hasTicket(long session);
private native int interruptBlockedIO(long ssl);
private native int getThreadsBlockedInPoll(long ssl);
/* ------------------- session-specific methods --------------------- */
@ -4125,6 +4182,49 @@ public class WolfSSLSession {
}
}
/**
* Interrupt native I/O operations blocked inside select()/poll().
*
* This is used by wolfJSSE when SSLSocket.close() is called, to wake up
* threads that are blocked in select()/poll().
*
* @return WolfSSL.SSL_SUCCESS on success, negative on error.
*
* @throws IllegalStateException WolfSSLSession has been freed
*/
public synchronized int interruptBlockedIO()
throws IllegalStateException {
confirmObjectIsActive();
/* Not synchronizing on sslLock, since we want to interrupt threads
* blocked on I/O operations, which will already hold sslLock */
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
WolfSSLDebug.INFO, "entered interruptBlockedIO()");
return interruptBlockedIO(this.sslPtr);
}
/**
* Get count of threads currently blocked in select() or poll()
* at the native JNI level.
*
* @return count of threads waiting in select() or poll()
*
* @throws IllegalStateException WolfSSLSession has been freed
*/
public synchronized int getThreadsBlockedInPoll()
throws IllegalStateException {
confirmObjectIsActive();
WolfSSLDebug.log(getClass(), WolfSSLDebug.Component.JNI,
WolfSSLDebug.INFO, "entered getThreadsBlockedInPoll()");
return getThreadsBlockedInPoll(this.sslPtr);
}
/**
* Use SNI name with this session.
*

View File

@ -313,7 +313,7 @@ public class WolfSSLEngine extends SSLEngine {
}
/* will throw WolfSSLException if issue creating WOLFSSL */
ssl = new WolfSSLSession(ctx);
ssl = new WolfSSLSession(ctx, false);
enableExtraDebug();
enableIODebug();

View File

@ -2011,6 +2011,10 @@ public class WolfSSLSocket extends SSLSocket {
handshakeFinished = this.handshakeComplete;
}
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"signaling any blocked I/O threads to wake up");
ssl.interruptBlockedIO();
/* Try TLS shutdown procedure, only if handshake has finished */
if (ssl != null && handshakeFinished) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
@ -2036,12 +2040,15 @@ public class WolfSSLSocket extends SSLSocket {
}
try {
/* Use SO_LINGER value when calling
* shutdown here, since we are closing the
* socket */
if (this.socket != null) {
ret = ssl.shutdownSSL(
this.socket.getSoTimeout());
this.socket.getSoLinger());
} else {
ret = ssl.shutdownSSL(
super.getSoTimeout());
super.getSoLinger());
}
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
@ -2065,9 +2072,7 @@ public class WolfSSLSocket extends SSLSocket {
/* Release native verify callback (JNI global) */
this.EngineHelper.unsetVerifyCallback();
/* Connection is closed, free native WOLFSSL session
* to release native memory earlier than garbage
* collector might with finalize(). */
/* Close ConsumedRecvCtx data stream */
Object readCtx = this.ssl.getIOReadCtx();
if (readCtx != null &&
readCtx instanceof ConsumedRecvCtx) {
@ -2076,14 +2081,6 @@ public class WolfSSLSocket extends SSLSocket {
"calling ConsumedRecvCtx.closeDataStreams()");
rctx.closeDataStreams();
}
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"calling this.ssl.freeSSL()");
this.ssl.freeSSL();
this.ssl = null;
/* Reset internal WolfSSLEngineHelper to null */
this.EngineHelper.clearObjectState();
this.EngineHelper = null;
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"thread exiting handshakeLock (shutdown)");
@ -2112,6 +2109,33 @@ public class WolfSSLSocket extends SSLSocket {
this.outStream = null;
}
}
/* Free this.ssl here instead of above for use cases
* where a SSLSocket is created then closed()'d before
* connected or handshake is done. freeSSL() will
* release interruptFds[] pipe() and free up descriptor. */
synchronized (ioLock) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"thread got ioLock (freeSSL)");
/* Connection is closed, free native WOLFSSL session
* to release native memory earlier than garbage
* collector might with finalize(), if we don't
* have any threads still waiting in poll/select. */
if (this.ssl.getThreadsBlockedInPoll() == 0) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"calling this.ssl.freeSSL()");
this.ssl.freeSSL();
this.ssl = null;
}
/* Reset internal WolfSSLEngineHelper to null */
this.EngineHelper.clearObjectState();
this.EngineHelper = null;
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
"thread exiting ioLock (shutdown)");
} /* ioLock */
}
if (this.autoClose) {
@ -2687,7 +2711,11 @@ public class WolfSSLSocket extends SSLSocket {
throw e;
} catch (IllegalStateException e) {
throw new IOException(e);
/* SSLSocket.close() may have already called freeSSL(),
* thus causing a 'WolfSSLSession object has been freed'
* IllegalStateException to be thrown from
* WolfSSLSession.read(). Return as a SocketException here. */
throw new SocketException(e.getMessage());
}
/* return number of bytes read */

View File

@ -59,6 +59,7 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
@ -116,6 +117,8 @@ import com.wolfssl.WolfSSLException;
public void testSessionResumptionWithTicketEnabled();
public void testDoubleSocketClose();
public void testSocketConnectException();
public void testSocketCloseInterruptsWrite();
public void testSocketCloseInterruptsRead();
*/
public class WolfSSLSocketTest {
@ -465,6 +468,8 @@ public class WolfSSLSocketTest {
TestClient client = null;
Exception srvException = null;
Exception cliException = null;
CountDownLatch sDoneLatch = null;
CountDownLatch cDoneLatch = null;
System.out.print("\twolfjsse.enabledSupportedCurves");
@ -485,11 +490,19 @@ public class WolfSSLSocketTest {
TestArgs sArgs = new TestArgs(null, null, true, true, true, null);
TestArgs cArgs = new TestArgs(null, null, false, false, true, null);
server = new TestServer(this.ctx, ss, sArgs, 1);
sDoneLatch = new CountDownLatch(1);
cDoneLatch = new CountDownLatch(1);
server = new TestServer(this.ctx, ss, sArgs, 1, sDoneLatch);
server.start();
client = new TestClient(this.ctx, ss.getLocalPort(), cArgs);
client = new TestClient(this.ctx, ss.getLocalPort(), cArgs,
cDoneLatch);
client.start();
cDoneLatch.await();
sDoneLatch.await();
srvException = server.getException();
if (srvException != null) {
Security.setProperty("wolfjsse.enabledSupportedCurves",
@ -527,11 +540,19 @@ public class WolfSSLSocketTest {
TestArgs sArgs = new TestArgs(null, null, true, true, true, null);
TestArgs cArgs = new TestArgs(null, null, false, false, true, null);
server = new TestServer(this.ctx, ss, sArgs, 1);
sDoneLatch = new CountDownLatch(1);
cDoneLatch = new CountDownLatch(1);
server = new TestServer(this.ctx, ss, sArgs, 1, sDoneLatch);
server.start();
client = new TestClient(this.ctx, ss.getLocalPort(), cArgs);
client = new TestClient(this.ctx, ss.getLocalPort(), cArgs,
cDoneLatch);
client.start();
cDoneLatch.await();
sDoneLatch.await();
srvException = server.getException();
if (srvException != null) {
Security.setProperty("wolfjsse.enabledSupportedCurves",
@ -569,11 +590,19 @@ public class WolfSSLSocketTest {
TestArgs sArgs = new TestArgs(null, null, true, true, true, null);
TestArgs cArgs = new TestArgs(null, null, false, false, true, null);
server = new TestServer(this.ctx, ss, sArgs, 1);
sDoneLatch = new CountDownLatch(1);
cDoneLatch = new CountDownLatch(1);
server = new TestServer(this.ctx, ss, sArgs, 1, sDoneLatch);
server.start();
client = new TestClient(this.ctx, ss.getLocalPort(), cArgs);
client = new TestClient(this.ctx, ss.getLocalPort(), cArgs,
cDoneLatch);
client.start();
cDoneLatch.await();
sDoneLatch.await();
srvException = server.getException();
if (srvException != null) {
Security.setProperty("wolfjsse.enabledSupportedCurves",
@ -600,7 +629,9 @@ public class WolfSSLSocketTest {
}
}
/* Test with invalid property entries */
/* Test with invalid property entries.
* Only need to start client thread, since it throws exception
* before connecting to server. */
{
Security.setProperty("wolfjsse.enabledSupportedCurves",
"badone, badtwo");
@ -609,19 +640,15 @@ public class WolfSSLSocketTest {
ss = (SSLServerSocket)ctx.getServerSocketFactory()
.createServerSocket(0);
TestArgs sArgs = new TestArgs(null, null, true, true, true, null);
TestArgs cArgs = new TestArgs(null, null, false, false, true, null);
server = new TestServer(this.ctx, ss, sArgs, 1);
server.start();
client = new TestClient(this.ctx, ss.getLocalPort(), cArgs);
cDoneLatch = new CountDownLatch(1);
client = new TestClient(this.ctx, ss.getLocalPort(), cArgs,
cDoneLatch);
client.start();
srvException = server.getException();
if (srvException != null) {
Security.setProperty("wolfjsse.enabledSupportedCurves",
originalProperty);
throw srvException;
}
cDoneLatch.await();
cliException = client.getException();
if (cliException != null) {
@ -630,7 +657,7 @@ public class WolfSSLSocketTest {
try {
client.join(1000);
server.join(1000);
//server.join(1000);
} catch (InterruptedException e) {
System.out.println("interrupt happened");
@ -656,6 +683,9 @@ public class WolfSSLSocketTest {
@Test
public void testClientServerThreaded() throws Exception {
CountDownLatch sDoneLatch = null;
CountDownLatch cDoneLatch = null;
System.out.print("\tTesting basic client/server");
/* create new CTX */
@ -668,12 +698,18 @@ public class WolfSSLSocketTest {
TestArgs sArgs = new TestArgs(null, null, true, true, true, null);
TestArgs cArgs = new TestArgs(null, null, false, false, true, null);
TestServer server = new TestServer(this.ctx, ss, sArgs, 1);
sDoneLatch = new CountDownLatch(1);
cDoneLatch = new CountDownLatch(1);
TestServer server = new TestServer(this.ctx, ss, sArgs, 1, sDoneLatch);
server.start();
TestClient client = new TestClient(this.ctx, ss.getLocalPort(), cArgs);
TestClient client = new TestClient(this.ctx, ss.getLocalPort(), cArgs,
cDoneLatch);
client.start();
cDoneLatch.await();
sDoneLatch.await();
Exception srvException = server.getException();
if (srvException != null) {
@ -700,6 +736,9 @@ public class WolfSSLSocketTest {
public void alpnClientServerRunner(TestArgs sArgs, TestArgs cArgs,
boolean expectingException) throws Exception {
CountDownLatch sDoneLatch = null;
CountDownLatch cDoneLatch = null;
if (sArgs == null || cArgs == null) {
throw new Exception("client/server TestArgs can not be null");
}
@ -710,12 +749,19 @@ public class WolfSSLSocketTest {
SSLServerSocket ss = (SSLServerSocket)ctx.getServerSocketFactory()
.createServerSocket(0);
TestServer server = new TestServer(this.ctx, ss, sArgs, 1);
sDoneLatch = new CountDownLatch(1);
cDoneLatch = new CountDownLatch(1);
TestServer server = new TestServer(this.ctx, ss, sArgs, 1, sDoneLatch);
server.start();
TestClient client = new TestClient(this.ctx, ss.getLocalPort(), cArgs);
TestClient client = new TestClient(this.ctx, ss.getLocalPort(), cArgs,
cDoneLatch);
client.start();
cDoneLatch.await();
sDoneLatch.await();
try {
client.join(1000);
server.join(1000);
@ -988,7 +1034,7 @@ public class WolfSSLSocketTest {
/* This test hangs on Android, marking TODO for later investigation. Seems to be
* something specific to the test code, not library proper. */
if (tf.isAndroid()) {
if (WolfSSLTestFactory.isAndroid()) {
System.out.println("\t... skipped");
return;
}
@ -2746,6 +2792,271 @@ public class WolfSSLSocketTest {
System.out.println("\t... passed");
}
/* Test timeout set to 10000 ms (10 sec) in case inerrupt code is not
* working as expected, we will see the timeout as a hard error that
* this test has failed */
@Test(timeout = 10000)
public void testSocketCloseInterruptsWrite() throws Exception {
String protocol = null;
SSLServerSocketFactory ssf = null;
SSLServerSocket ss = null;
SSLSocketFactory sf = null;
boolean passed = false;
System.out.print("\tTesting close/write interrupt");
/* 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 */
if (WolfSSLTestFactory.isWindows()) {
System.out.println("\t... skipped");
return;
}
if (WolfSSL.TLSv12Enabled()) {
protocol = "TLSv1.2";
} else if (WolfSSL.TLSv11Enabled()) {
protocol = "TLSv1.1";
} else if (WolfSSL.TLSv1Enabled()) {
protocol = "TLSv1.0";
} else {
System.out.println("\t... skipped");
return;
}
/* create new CTX */
this.ctx = tf.createSSLContext(protocol, ctxProvider);
/* create SSLServerSocket first to get ephemeral port */
ss = (SSLServerSocket)ctx.getServerSocketFactory()
.createServerSocket(0);
final SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
cs.connect(new InetSocketAddress(ss.getLocalPort()));
final SSLSocket server = (SSLSocket)ss.accept();
final CountDownLatch closeLatch = new CountDownLatch(1);
ExecutorService es = Executors.newSingleThreadExecutor();
Future<Void> serverFuture = es.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
try {
server.startHandshake();
boolean doClose = closeLatch.await(90L, TimeUnit.SECONDS);
if (!doClose) {
/* Return without closing, latch not hit within
* time limit */
return null;
}
/* Sleep so write thread has a chance to do some
* writing before interrupt */
Thread.sleep(1000);
cs.setSoLinger(true, 5);
cs.close();
} catch (SSLException e) {
System.out.println("\t... failed");
e.printStackTrace();
fail("Server thread got SSLException when not expected");
}
return null;
}
});
byte[] tmpArr = new byte[1024];
Arrays.fill(tmpArr, (byte)0xA2);
OutputStream out = cs.getOutputStream();
try {
try {
cs.startHandshake();
out.write(tmpArr);
}
catch (Exception e) {
System.out.println("\t... failed");
e.printStackTrace();
fail("Exception from first out.write() when not expected");
}
try {
/* signal server thread to try and close socket */
closeLatch.countDown();
/* keep writing, we should get interrupted */
while (true) {
out.write(tmpArr);
}
} catch (SocketException e) {
/* We expect SocketException with this message, error if
* different than expected */
if (!e.getMessage().contains("Socket fd closed during poll")) {
System.out.println("\t... failed");
e.printStackTrace();
fail("Incorrect SocketException thrown by client");
throw e;
}
passed = true;
}
}
finally {
es.shutdown();
serverFuture.get();
if (!cs.isClosed()) {
cs.close();
}
if (!server.isClosed()) {
server.close();
}
if (!ss.isClosed()) {
ss.close();
}
}
if (passed) {
System.out.println("\t... passed");
}
}
/* Test timeout set to 10000 ms (10 sec) in case inerrupt code is not
* working as expected, we will see the timeout as a hard error that
* this test has failed */
@Test(timeout = 10000)
public void testSocketCloseInterruptsRead() throws Exception {
int ret = 0;
String protocol = null;
SSLServerSocketFactory ssf = null;
SSLServerSocket ss = null;
SSLSocketFactory sf = null;
boolean passed = false;
System.out.print("\tTesting close/read interrupt");
/* 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 */
if (WolfSSLTestFactory.isWindows()) {
System.out.println("\t... skipped");
return;
}
if (WolfSSL.TLSv12Enabled()) {
protocol = "TLSv1.2";
} else if (WolfSSL.TLSv11Enabled()) {
protocol = "TLSv1.1";
} else if (WolfSSL.TLSv1Enabled()) {
protocol = "TLSv1.0";
} else {
System.out.println("\t... skipped");
return;
}
/* create new CTX */
this.ctx = tf.createSSLContext(protocol, ctxProvider);
/* create SSLServerSocket first to get ephemeral port */
ss = (SSLServerSocket)ctx.getServerSocketFactory()
.createServerSocket(0);
final SSLSocket cs = (SSLSocket)ctx.getSocketFactory().createSocket();
cs.connect(new InetSocketAddress(ss.getLocalPort()));
final SSLSocket server = (SSLSocket)ss.accept();
final CountDownLatch closeLatch = new CountDownLatch(1);
ExecutorService es = Executors.newSingleThreadExecutor();
Future<Void> serverFuture = es.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
try {
server.startHandshake();
boolean doClose = closeLatch.await(90L, TimeUnit.SECONDS);
if (!doClose) {
/* Return without closing, latch not hit within
* time limit */
return null;
}
/* Sleep to let client thread hit read call */
Thread.sleep(1000);
cs.setSoLinger(true, 5);
cs.close();
} catch (SSLException e) {
System.out.println("\t... failed");
e.printStackTrace();
fail("Server thread got SSLException when not expected");
}
return null;
}
});
byte[] tmpArr = new byte[1024];
InputStream in = cs.getInputStream();
try {
try {
cs.startHandshake();
}
catch (Exception e) {
System.out.println("\t... failed");
e.printStackTrace();
fail("Exception from startHandshake() when not expected");
}
try {
/* signal server thread to try and close socket */
closeLatch.countDown();
while (true) {
ret = in.read(tmpArr, 0, tmpArr.length);
if (ret == -1) {
/* end of stream */
break;
}
}
} catch (SocketException e) {
/* We expect SocketException with this message, error if
* different than expected */
if (!e.getMessage().contains("Socket is closed") &&
!e.getMessage().contains("Connection already shutdown") &&
!e.getMessage().contains("object has been freed")) {
System.out.println("\t... failed");
e.printStackTrace();
fail("Incorrect SocketException thrown by client");
throw e;
}
}
passed = true;
}
finally {
es.shutdown();
serverFuture.get();
if (!cs.isClosed()) {
cs.close();
}
if (!server.isClosed()) {
server.close();
}
if (!ss.isClosed()) {
ss.close();
}
}
if (passed) {
System.out.println("\t... passed");
}
}
@Test
public void testSocketMethodsAfterClose() throws Exception {
@ -3089,13 +3400,15 @@ public class WolfSSLSocketTest {
private int numConnections = 1;
WolfSSLSocketTest wst;
SSLServerSocket ss = null;
CountDownLatch doneLatch = null;
public TestServer(SSLContext ctx, SSLServerSocket ss,
TestArgs args, int numConnections) {
TestArgs args, int numConnections, CountDownLatch doneLatch) {
this.ctx = ctx;
this.ss = ss;
this.args = args;
this.numConnections = numConnections;
this.doneLatch = doneLatch;
}
@ -3175,6 +3488,8 @@ public class WolfSSLSocketTest {
} catch (Exception e) {
this.exception = e;
} finally {
this.doneLatch.countDown();
}
}
@ -3202,11 +3517,14 @@ public class WolfSSLSocketTest {
private Exception exception = null;
private TestArgs args = null;
WolfSSLSocketTest wst;
CountDownLatch doneLatch = null;
public TestClient(SSLContext ctx, int port, TestArgs args) {
public TestClient(SSLContext ctx, int port, TestArgs args,
CountDownLatch doneLatch) {
this.ctx = ctx;
this.srvPort = port;
this.args = args;
this.doneLatch = doneLatch;
}
@Override
@ -3271,6 +3589,8 @@ public class WolfSSLSocketTest {
} catch (Exception e) {
this.exception = e;
} finally {
this.doneLatch.countDown();
}
}

View File

@ -1052,13 +1052,24 @@ class WolfSSLTestFactory {
* Test if the env is Android
* @return true if is Android system
*/
protected boolean isAndroid() {
protected static boolean isAndroid() {
if (System.getProperty("java.runtime.name").contains("Android")) {
return true;
}
return false;
}
/**
* Test if the env is Windows.
* @return true if Windows, otherwise false
*/
protected static boolean isWindows() {
if (System.getProperty("os.name").startsWith("Windows")) {
return true;
}
return false;
}
/**
* Check if Security property contains a specific value.
*

View File

@ -121,7 +121,7 @@ public class WolfSSLTrustX509Test {
System.out.print("\tTesting parse all.jks");
if (tf.isAndroid()) {
if (WolfSSLTestFactory.isAndroid()) {
/* @TODO finding that BKS has different order of certs */
pass("\t... skipped");
return;
@ -186,7 +186,7 @@ public class WolfSSLTrustX509Test {
System.out.print("\tTesting use before init()");
if (tf.isAndroid()) {
if (WolfSSLTestFactory.isAndroid()) {
/* @TODO finding that BKS has different order of certs */
pass("\t... skipped");
return;
@ -242,7 +242,7 @@ public class WolfSSLTrustX509Test {
System.out.print("\tTesting parsing server.jks");
if (tf.isAndroid()) {
if (WolfSSLTestFactory.isAndroid()) {
/* @TODO finding that BKS has different order of certs */
pass("\t... skipped");
return;
@ -312,7 +312,7 @@ public class WolfSSLTrustX509Test {
System.out.print("\tTesting parse all_mixed.jks");
if (tf.isAndroid()) {
if (WolfSSLTestFactory.isAndroid()) {
/* @TODO finding that BKS has different order of certs */
pass("\t... skipped");
return;
@ -570,7 +570,7 @@ public class WolfSSLTrustX509Test {
String eccInt1Cert = "examples/certs/intermediate/ca-int-ecc-cert.pem";
String eccInt2Cert = "examples/certs/intermediate/ca-int2-ecc-cert.pem";
if (tf.isAndroid()) {
if (WolfSSLTestFactory.isAndroid()) {
rsaServerCert = "/sdcard/" + rsaServerCert;
rsaInt1Cert = "/sdcard/" + rsaInt1Cert;
rsaInt2Cert = "/sdcard/" + rsaInt2Cert;
@ -697,7 +697,7 @@ public class WolfSSLTrustX509Test {
"examples/certs/intermediate/ca-int-cert.pem";
String eccInt2Cert = "examples/certs/intermediate/ca-int2-ecc-cert.pem";
if (tf.isAndroid()) {
if (WolfSSLTestFactory.isAndroid()) {
rsaServerCert = "/sdcard/" + rsaServerCert;
rsaInt1CertWrong = "/sdcard/" + rsaInt1CertWrong;
rsaInt2Cert = "/sdcard/" + rsaInt2Cert;
@ -825,7 +825,7 @@ public class WolfSSLTrustX509Test {
String eccInt1Cert = "examples/certs/intermediate/ca-int-ecc-cert.pem";
String eccInt2Cert = "examples/certs/intermediate/ca-int2-ecc-cert.pem";
if (tf.isAndroid()) {
if (WolfSSLTestFactory.isAndroid()) {
rsaServerCert = "/sdcard/" + rsaServerCert;
rsaInt1Cert = "/sdcard/" + rsaInt1Cert;
rsaInt2Cert = "/sdcard/" + rsaInt2Cert;
@ -948,7 +948,7 @@ public class WolfSSLTrustX509Test {
"examples/certs/intermediate/server-int-ecc-cert.pem";
String eccInt2Cert = "examples/certs/intermediate/ca-int2-ecc-cert.pem";
if (tf.isAndroid()) {
if (WolfSSLTestFactory.isAndroid()) {
rsaServerCert = "/sdcard/" + rsaServerCert;
rsaInt2Cert = "/sdcard/" + rsaInt2Cert;
@ -1054,7 +1054,7 @@ public class WolfSSLTrustX509Test {
String eccInt1Cert = "examples/certs/intermediate/ca-int-ecc-cert.pem";
String eccInt2Cert = "examples/certs/intermediate/ca-int2-ecc-cert.pem";
if (tf.isAndroid()) {
if (WolfSSLTestFactory.isAndroid()) {
rsaServerCert = "/sdcard/" + rsaServerCert;
rsaInt1Cert = "/sdcard/" + rsaInt1Cert;
rsaInt2Cert = "/sdcard/" + rsaInt2Cert;
@ -1180,7 +1180,7 @@ public class WolfSSLTrustX509Test {
String eccInt1Cert = "examples/certs/intermediate/ca-int-ecc-cert.pem";
String eccInt2Cert = "examples/certs/intermediate/ca-int2-ecc-cert.pem";
if (tf.isAndroid()) {
if (WolfSSLTestFactory.isAndroid()) {
rsaServerCert = "/sdcard/" + rsaServerCert;
rsaInt1Cert = "/sdcard/" + rsaInt1Cert;
rsaInt2Cert = "/sdcard/" + rsaInt2Cert;
@ -1340,7 +1340,7 @@ public class WolfSSLTrustX509Test {
String eccInt1Cert = "examples/certs/intermediate/ca-int-ecc-cert.pem";
String eccRootCert = "examples/certs/ca-ecc-cert.pem";
if (tf.isAndroid()) {
if (WolfSSLTestFactory.isAndroid()) {
rsaServerCert = "/sdcard/" + rsaServerCert;
rsaInt1Cert = "/sdcard/" + rsaInt1Cert;
rsaInt2Cert = "/sdcard/" + rsaInt2Cert;

View File

@ -363,7 +363,7 @@ public class WolfSSLX509Test {
break;
}
}
if (sigProvider == null || tf.isAndroid()) {
if (sigProvider == null || WolfSSLTestFactory.isAndroid()) {
pass("\t\t\t... skipped");
return;
}
@ -511,7 +511,7 @@ public class WolfSSLX509Test {
}
/* Android KeyStore formats x509 getName() differently than peer getName() */
if (!tf.isAndroid()) {
if (!WolfSSLTestFactory.isAndroid()) {
if (!x509.getSubjectDN().getName().equals(
peer.getSubjectDN().getName())) {
error("\t\t... failed");