mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 06:11:37 +00:00
282732 read/write/accept/close thread protection
This commit is contained in:
parent
be752e9b17
commit
a7873159bf
@ -1,7 +1,7 @@
|
||||
;+#
|
||||
;+# ***** BEGIN LICENSE BLOCK *****
|
||||
;+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
;+#
|
||||
LIBRARY jss4 ;-
|
||||
;+# The contents of this file are subject to the Mozilla Public License Version
|
||||
;+# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
;+# the License. You may obtain a copy of the License at
|
||||
@ -50,7 +50,7 @@
|
||||
|
||||
;+JSS_3.0 { # JSS 3.0 release
|
||||
;+ global:
|
||||
LIBRARY jss3 ;-
|
||||
LIBRARY jss4 ;-
|
||||
EXPORTS ;-
|
||||
Java_org_mozilla_jss_crypto_EncryptionAlgorithm_getIVLength;
|
||||
Java_org_mozilla_jss_crypto_PQGParams_generateNative__I;
|
||||
@ -284,3 +284,10 @@ Java_org_mozilla_jss_SecretDecoderRing_KeyManager_lookupUniqueNamedKeyNative;
|
||||
;+ local:
|
||||
;+ *;
|
||||
;+};
|
||||
;+JSS_4.1 { # JSS 4.1 release
|
||||
;+ global:
|
||||
Java_org_mozilla_jss_ssl_SSLSocket_abortReadWrite;
|
||||
Java_org_mozilla_jss_ssl_SSLServerSocket_abortAccept;
|
||||
;+ local:
|
||||
;+ *;
|
||||
;+};
|
||||
|
@ -81,6 +81,7 @@ Java_org_mozilla_jss_ssl_SSLServerSocket_socketAccept
|
||||
JSSL_SocketData *newSD=NULL;
|
||||
jbyteArray sdArray = NULL;
|
||||
SECStatus status;
|
||||
PRThread *me;
|
||||
|
||||
if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) goto finish;
|
||||
|
||||
@ -96,41 +97,37 @@ Java_org_mozilla_jss_ssl_SSLServerSocket_socketAccept
|
||||
}
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
newFD = PR_Accept(sock->fd, &addr, ivtimeout);
|
||||
/* Set the current thread doing the accept. */
|
||||
me = PR_GetCurrentThread();
|
||||
PR_Lock(sock->lock);
|
||||
PR_ASSERT(sock->accepter == NULL);
|
||||
sock->accepter = me;
|
||||
PR_Unlock(sock->lock);
|
||||
|
||||
if( newFD != NULL ) {
|
||||
/* success! */
|
||||
break;
|
||||
} else {
|
||||
switch( PR_GetError() ) {
|
||||
case PR_PENDING_INTERRUPT_ERROR:
|
||||
case PR_IO_PENDING_ERROR:
|
||||
break; /* out of the switch and loop again */
|
||||
newFD = PR_Accept(sock->fd, &addr, ivtimeout);
|
||||
|
||||
PR_Lock(sock->lock);
|
||||
PR_ASSERT(sock->accepter == me);
|
||||
sock->accepter = NULL;
|
||||
PR_Unlock(sock->lock);
|
||||
if( newFD == NULL ) {
|
||||
#ifdef WINNT
|
||||
case PR_IO_TIMEOUT_ERROR:
|
||||
/*
|
||||
* if timeout was set, and the PR_Accept() timed out,
|
||||
* then cancel the I/O on the port, otherwise PR_Accept()
|
||||
* will always return PR_IO_PENDING_ERROR on subsequent
|
||||
* calls
|
||||
*/
|
||||
PR_NT_CancelIo(sock->fd);
|
||||
/* don't break here, let it fall through */
|
||||
#endif
|
||||
default:
|
||||
JSSL_throwSSLSocketException(env,
|
||||
"Failed to accept new connection");
|
||||
goto finish;
|
||||
}
|
||||
PRErrorCode err = PR_GetError();
|
||||
if( err == PR_PENDING_INTERRUPT_ERROR ||
|
||||
err == PR_IO_TIMEOUT_ERROR ) {
|
||||
PR_NT_CancelIo(sock->fd);
|
||||
}
|
||||
#endif
|
||||
JSSL_throwSSLSocketException(env,
|
||||
"Failed to accept new connection");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
newSD = JSSL_CreateSocketData(env, newSock, newFD, NULL /* priv */);
|
||||
newFD = NULL;
|
||||
if( newSD == NULL ) {
|
||||
goto finish;
|
||||
}
|
||||
newFD = NULL;
|
||||
|
||||
/* setup the handshake callback */
|
||||
status = SSL_HandshakeCallback(newSD->fd, JSSL_HandshakeCallback,
|
||||
@ -138,6 +135,7 @@ Java_org_mozilla_jss_ssl_SSLServerSocket_socketAccept
|
||||
if( status != SECSuccess ) {
|
||||
JSSL_throwSSLSocketException(env,
|
||||
"Unable to install handshake callback");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* pass the pointer back to Java */
|
||||
@ -159,6 +157,30 @@ finish:
|
||||
return sdArray;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_mozilla_jss_ssl_SSLServerSocket_abortAccept(
|
||||
JNIEnv *env, jobject self)
|
||||
{
|
||||
JSSL_SocketData *sock = NULL;
|
||||
|
||||
if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS) goto finish;
|
||||
|
||||
/*
|
||||
* The java layer prevents I/O once close has been
|
||||
* called but if an accept is in progress then abort it.
|
||||
* For WINNT the accept method must check for
|
||||
* PR_PENDING_INTERRUPT_ERROR and call PR_NT_CancelIo.
|
||||
*/
|
||||
PR_Lock(sock->lock);
|
||||
if ( sock->accepter ) {
|
||||
PR_Interrupt(sock->accepter);
|
||||
}
|
||||
PR_Unlock(sock->lock);
|
||||
finish:
|
||||
EXCEPTION_CHECK(env, sock)
|
||||
return;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_mozilla_jss_ssl_SSLServerSocket_clearSessionCache(
|
||||
JNIEnv *env, jclass clazz)
|
||||
|
@ -49,6 +49,22 @@ import org.mozilla.jss.crypto.TokenException;
|
||||
*/
|
||||
public class SSLServerSocket extends java.net.ServerSocket {
|
||||
|
||||
/*
|
||||
* Locking rules of SSLServerSocket
|
||||
*
|
||||
* isClosed and inAccept must be accessed with the object locked.
|
||||
*
|
||||
* acceptLock must be locked throughout the accept method. It is
|
||||
* used to serialize accept calls on the object.
|
||||
*/
|
||||
|
||||
private SocketProxy sockProxy = null;
|
||||
private boolean handshakeAsClient = false;
|
||||
private SocketBase base = new SocketBase();
|
||||
private boolean isClosed = false;
|
||||
private boolean inAccept = false;
|
||||
private java.lang.Object acceptLock = new java.lang.Object();
|
||||
|
||||
/**
|
||||
* The default size of the listen queue.
|
||||
*/
|
||||
@ -140,10 +156,6 @@ public class SSLServerSocket extends java.net.ServerSocket {
|
||||
socketListen(backlog);
|
||||
}
|
||||
|
||||
private SocketProxy sockProxy;
|
||||
private boolean handshakeAsClient=false;
|
||||
private SocketBase base = new SocketBase();
|
||||
|
||||
private native void socketListen(int backlog) throws SocketException;
|
||||
|
||||
private static InetAddress anyLocalAddr;
|
||||
@ -158,14 +170,37 @@ public class SSLServerSocket extends java.net.ServerSocket {
|
||||
* or the timeout is reached.
|
||||
*/
|
||||
public Socket accept() throws IOException {
|
||||
SSLSocket s = new SSLSocket();
|
||||
/* socketAccept can throw an exception for timeouts or IO errors */
|
||||
/* so first get a socket pointer, and if successful create the SocketProxy */
|
||||
byte[] socketPointer = null;
|
||||
socketPointer = socketAccept(s, base.getTimeout(), handshakeAsClient);
|
||||
SocketProxy sp = new SocketProxy(socketPointer );
|
||||
s.setSockProxy(sp);
|
||||
return s;
|
||||
synchronized (acceptLock) {
|
||||
synchronized (this) {
|
||||
if (isClosed) {
|
||||
throw new IOException(
|
||||
"SSLServerSocket has been closed, and cannot be reused.");
|
||||
}
|
||||
inAccept = true;
|
||||
}
|
||||
SSLSocket s = new SSLSocket();
|
||||
try {
|
||||
/*
|
||||
* socketAccept can throw an exception for timeouts,
|
||||
* IO errors, or PR_Interrupt called by abortAccept.
|
||||
* So first get a socket pointer, and if successful
|
||||
* create the SocketProxy.
|
||||
*/
|
||||
byte[] socketPointer = null;
|
||||
socketPointer = socketAccept(s, base.getTimeout(),
|
||||
handshakeAsClient);
|
||||
SocketProxy sp = new SocketProxy(socketPointer);
|
||||
s.setSockProxy(sp);
|
||||
} catch (Exception e) {
|
||||
/* unnessary to do a s.close() since exception thrown*/
|
||||
throw new IOException("accept method failed");
|
||||
} finally {
|
||||
synchronized (this) {
|
||||
inAccept=false;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -186,7 +221,7 @@ public class SSLServerSocket extends java.net.ServerSocket {
|
||||
|
||||
public native void setReuseAddress(boolean reuse) throws SocketException;
|
||||
public native boolean getReuseAddress() throws SocketException;
|
||||
|
||||
private native void abortAccept() throws SocketException;
|
||||
private native byte[] socketAccept(SSLSocket s, int timeout,
|
||||
boolean handshakeAsClient) throws SocketException;
|
||||
|
||||
@ -195,7 +230,9 @@ public class SSLServerSocket extends java.net.ServerSocket {
|
||||
*/
|
||||
public static native void clearSessionCache();
|
||||
|
||||
protected void finalize() throws Throwable { }
|
||||
protected void finalize() throws Throwable {
|
||||
close(); /* in case user never called close */
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -209,7 +246,26 @@ public class SSLServerSocket extends java.net.ServerSocket {
|
||||
* Closes this socket.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
base.close();
|
||||
synchronized (this) {
|
||||
if( isClosed ) {
|
||||
/* finalize calls close or user calls close more than once */
|
||||
return;
|
||||
}
|
||||
isClosed = true;
|
||||
if( sockProxy == null ) {
|
||||
/* nothing to do */
|
||||
return;
|
||||
}
|
||||
if( inAccept ) {
|
||||
abortAccept();
|
||||
}
|
||||
}
|
||||
/* Lock acceptLock to ensure that accept has been aborted. */
|
||||
synchronized (acceptLock) {
|
||||
base.close();
|
||||
sockProxy = null;
|
||||
base.setProxy(null);
|
||||
}
|
||||
}
|
||||
|
||||
// This directory is used as the default for the Session ID cache
|
||||
|
@ -39,7 +39,6 @@
|
||||
#include <ssl.h>
|
||||
#include <sslerr.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <jssutil.h>
|
||||
#include <jss_exceptions.h>
|
||||
#include <java_ids.h>
|
||||
@ -686,13 +685,9 @@ Java_org_mozilla_jss_ssl_SSLSocket_socketRead(JNIEnv *env, jobject self,
|
||||
jbyte *buf = NULL;
|
||||
jint size;
|
||||
PRIntervalTime ivtimeout;
|
||||
PRThread *me;
|
||||
jint nread;
|
||||
|
||||
/* get the socket */
|
||||
if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
|
||||
size = (*env)->GetArrayLength(env, bufBA);
|
||||
if( off < 0 || len < 0 || (off+len) > size) {
|
||||
JSS_throw(env, INDEX_OUT_OF_BOUNDS_EXCEPTION);
|
||||
@ -707,42 +702,49 @@ Java_org_mozilla_jss_ssl_SSLSocket_socketRead(JNIEnv *env, jobject self,
|
||||
ivtimeout = (timeout > 0) ? PR_MillisecondsToInterval(timeout)
|
||||
: PR_INTERVAL_NO_TIMEOUT;
|
||||
|
||||
for(;;) {
|
||||
nread = PR_Recv(sock->fd, buf+off, len, 0 /*flags*/, ivtimeout);
|
||||
/* get the socket */
|
||||
if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if( nread >= 0 ) {
|
||||
/* either we read some bytes, or we hit EOF. Either way, we're
|
||||
done */
|
||||
break;
|
||||
} else {
|
||||
/* some error, but is it recoverable? */
|
||||
PRErrorCode err = PR_GetError();
|
||||
/* set the current thread doing the read */
|
||||
me = PR_GetCurrentThread();
|
||||
PR_Lock(sock->lock);
|
||||
PR_ASSERT(sock->reader == NULL);
|
||||
sock->reader = me;
|
||||
PR_Unlock(sock->lock);
|
||||
|
||||
nread = PR_Recv(sock->fd, buf+off, len, 0 /*flags*/, ivtimeout);
|
||||
|
||||
if( err == PR_PENDING_INTERRUPT_ERROR ||
|
||||
err == PR_IO_PENDING_ERROR )
|
||||
{
|
||||
/* just try again */
|
||||
} else {
|
||||
PR_Lock(sock->lock);
|
||||
PR_ASSERT(sock->reader == me);
|
||||
sock->reader = NULL;
|
||||
PR_Unlock(sock->lock);
|
||||
|
||||
if( nread < 0 ) {
|
||||
PRErrorCode err = PR_GetError();
|
||||
|
||||
if( err == PR_PENDING_INTERRUPT_ERROR ) {
|
||||
#ifdef WINNT
|
||||
if (err == PR_IO_TIMEOUT_ERROR ) {
|
||||
/*
|
||||
* if timeout was set, and the PR_Accept() timed out,
|
||||
* then cancel the I/O on the port, otherwise PR_Accept()
|
||||
* will always return PR_IO_PENDING_ERROR on subsequent
|
||||
* calls
|
||||
*/
|
||||
PR_NT_CancelIo(sock->fd);
|
||||
JSSL_throwSSLSocketException(env, "Operation timed out");
|
||||
goto finish;
|
||||
}
|
||||
/* Clean up after PR_interrupt called by abortReadWrite. */
|
||||
PR_NT_CancelIo(sock->fd);
|
||||
#endif
|
||||
/* unrecoverable error */
|
||||
JSSL_throwSSLSocketException(env,
|
||||
"Error reading from socket");
|
||||
goto finish;
|
||||
}
|
||||
JSSL_throwSSLSocketException(env, "Read operation interrupted");
|
||||
} else if( err == PR_IO_TIMEOUT_ERROR ) {
|
||||
#ifdef WINNT
|
||||
/*
|
||||
* if timeout was set, and the PR_Recv timed out,
|
||||
* then cancel the I/O on the socket, otherwise PR_Recv()
|
||||
* will always return PR_IO_PENDING_ERROR on subsequent
|
||||
* calls
|
||||
*/
|
||||
PR_NT_CancelIo(sock->fd);
|
||||
#endif
|
||||
JSSL_throwSSLSocketException(env, "Operation timed out");
|
||||
} else {
|
||||
JSSL_throwSSLSocketException(env, "Error reading from socket");
|
||||
}
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if( nread == 0 ) {
|
||||
@ -784,6 +786,8 @@ Java_org_mozilla_jss_ssl_SSLSocket_socketWrite(JNIEnv *env, jobject self,
|
||||
jbyte *buf = NULL;
|
||||
jint size;
|
||||
PRIntervalTime ivtimeout;
|
||||
PRThread *me;
|
||||
PRInt32 numwrit;
|
||||
|
||||
if( bufBA == NULL ) {
|
||||
JSS_throw(env, NULL_POINTER_EXCEPTION);
|
||||
@ -796,10 +800,6 @@ Java_org_mozilla_jss_ssl_SSLSocket_socketWrite(JNIEnv *env, jobject self,
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
buf = (*env)->GetByteArrayElements(env, bufBA, NULL);
|
||||
if( buf == NULL ) {
|
||||
goto finish;
|
||||
@ -808,40 +808,50 @@ Java_org_mozilla_jss_ssl_SSLSocket_socketWrite(JNIEnv *env, jobject self,
|
||||
ivtimeout = (timeout > 0) ? PR_MillisecondsToInterval(timeout)
|
||||
: PR_INTERVAL_NO_TIMEOUT;
|
||||
|
||||
for(;;) {
|
||||
PRInt32 numwrit;
|
||||
|
||||
numwrit = PR_Send(sock->fd, buf+off, len, 0 /*flags*/, ivtimeout);
|
||||
|
||||
if( numwrit < 0 ) {
|
||||
PRErrorCode err = PR_GetError();
|
||||
if( err == PR_PENDING_INTERRUPT_ERROR ||
|
||||
err == PR_IO_PENDING_ERROR)
|
||||
{
|
||||
/* just try again */
|
||||
} else if( err == PR_IO_TIMEOUT_ERROR ) {
|
||||
#ifdef WINNT
|
||||
/*
|
||||
* if timeout was set, and the PR_Accept() timed out,
|
||||
* then cancel the I/O on the port, otherwise PR_Accept()
|
||||
* will always return PR_IO_PENDING_ERROR on subsequent
|
||||
* calls
|
||||
*/
|
||||
PR_NT_CancelIo(sock->fd);
|
||||
#endif
|
||||
JSSL_throwSSLSocketException(env, "Operation timed out");
|
||||
goto finish;
|
||||
} else {
|
||||
JSSL_throwSSLSocketException(env,
|
||||
"Failed to write to socket");
|
||||
goto finish;
|
||||
}
|
||||
} else {
|
||||
/* PR_Send is supposed to block until it sends everything */
|
||||
PR_ASSERT(numwrit == len);
|
||||
break;
|
||||
}
|
||||
/* get the socket */
|
||||
if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) {
|
||||
goto finish;
|
||||
}
|
||||
/* set the current thread doing the write */
|
||||
me = PR_GetCurrentThread();
|
||||
PR_Lock(sock->lock);
|
||||
PR_ASSERT(sock->writer == NULL);
|
||||
sock->writer = me;
|
||||
PR_Unlock(sock->lock);
|
||||
|
||||
numwrit = PR_Send(sock->fd, buf+off, len, 0 /*flags*/, ivtimeout);
|
||||
|
||||
PR_Lock(sock->lock);
|
||||
PR_ASSERT(sock->writer == me);
|
||||
sock->writer = NULL;
|
||||
PR_Unlock(sock->lock);
|
||||
|
||||
if( numwrit < 0 ) {
|
||||
PRErrorCode err = PR_GetError();
|
||||
if( err == PR_PENDING_INTERRUPT_ERROR ) {
|
||||
#ifdef WINNT
|
||||
/* clean up after PR_Interrupt called by abortReadWrite. */
|
||||
PR_NT_CancelIo(sock->fd);
|
||||
#endif
|
||||
JSSL_throwSSLSocketException(env, "Write operation interrupted");
|
||||
} else if( err == PR_IO_TIMEOUT_ERROR ) {
|
||||
#ifdef WINNT
|
||||
/*
|
||||
* if timeout was set, and the PR_Send() timed out,
|
||||
* then cancel the I/O on the socket, otherwise PR_Send()
|
||||
* will always return PR_IO_PENDING_ERROR on subsequent
|
||||
* calls
|
||||
*/
|
||||
PR_NT_CancelIo(sock->fd);
|
||||
#endif
|
||||
JSSL_throwSSLSocketException(env, "Operation timed out");
|
||||
} else {
|
||||
JSSL_throwSSLSocketException(env, "Failed to write to socket");
|
||||
}
|
||||
goto finish;
|
||||
}
|
||||
/* PR_Send is supposed to block until it sends everything */
|
||||
PR_ASSERT(numwrit == len);
|
||||
|
||||
finish:
|
||||
if( buf != NULL ) {
|
||||
@ -850,6 +860,33 @@ finish:
|
||||
EXCEPTION_CHECK(env, sock)
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_mozilla_jss_ssl_SSLSocket_abortReadWrite(
|
||||
JNIEnv *env, jobject self)
|
||||
{
|
||||
JSSL_SocketData *sock = NULL;
|
||||
|
||||
if( JSSL_getSockData(env, self, &sock) != PR_SUCCESS ) goto finish;
|
||||
|
||||
/*
|
||||
* The java layer prevents I/O once close has been
|
||||
* called but if an I/O operation is in progress then abort it.
|
||||
* For WINNT the read and write methods must check for the
|
||||
* PR_PENDING_INTERRUPT_ERROR and call PR_NT_CancelIo.
|
||||
*/
|
||||
PR_Lock(sock->lock);
|
||||
if ( sock->reader ) {
|
||||
PR_Interrupt(sock->reader);
|
||||
}
|
||||
if ( sock->writer ) {
|
||||
PR_Interrupt(sock->writer);
|
||||
}
|
||||
PR_Unlock(sock->lock);
|
||||
finish:
|
||||
EXCEPTION_CHECK(env, sock)
|
||||
return;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_mozilla_jss_ssl_SSLSocket_shutdownNative(
|
||||
JNIEnv *env, jobject self, jint how)
|
||||
|
@ -49,6 +49,31 @@ import java.security.AccessController;
|
||||
*/
|
||||
public class SSLSocket extends java.net.Socket {
|
||||
|
||||
/*
|
||||
* Locking strategy of SSLSocket
|
||||
*
|
||||
* isClosed, inRead, and inWrite must be accessed with the object
|
||||
* locked.
|
||||
*
|
||||
* readLock must be locked throughout the read method. It is used
|
||||
* to serialize read calls.
|
||||
*
|
||||
* writeLock must be locked throughout the write method. It is used
|
||||
* to serialize write calls.
|
||||
*/
|
||||
|
||||
private java.lang.Object readLock = new java.lang.Object();
|
||||
private java.lang.Object writeLock = new java.lang.Object();
|
||||
private boolean isClosed = false;
|
||||
private boolean inRead = false;
|
||||
private boolean inWrite = false;
|
||||
private InetAddress inetAddress;
|
||||
private int port;
|
||||
private SocketProxy sockProxy = null;
|
||||
private boolean open = false;
|
||||
private boolean handshakeAsClient = true;
|
||||
private SocketBase base = new SocketBase();
|
||||
|
||||
/**
|
||||
* For sockets that get created by accept().
|
||||
*/
|
||||
@ -338,7 +363,7 @@ public class SSLSocket extends java.net.Socket {
|
||||
}
|
||||
|
||||
private native void shutdownNative(int how) throws IOException;
|
||||
|
||||
private native void abortReadWrite() throws IOException;
|
||||
/**
|
||||
* Sets the SO_LINGER socket option.
|
||||
* param linger The time (in seconds) to linger for.
|
||||
@ -391,7 +416,36 @@ public class SSLSocket extends java.net.Socket {
|
||||
* Closes this socket.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
base.close();
|
||||
synchronized (this) {
|
||||
if( isClosed ) {
|
||||
/* finalize calls close or user calls close more than once */
|
||||
return;
|
||||
}
|
||||
isClosed = true;
|
||||
if( sockProxy == null ) {
|
||||
/* nothing to do */
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* If a read or write is occuring, abort the I/O. Any
|
||||
* further attempts to read/write will fail since isClosed
|
||||
* is true
|
||||
*/
|
||||
if ( inRead || inWrite ) {
|
||||
abortReadWrite();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Lock readLock and writeLock to ensure that read and write
|
||||
* have been aborted.
|
||||
*/
|
||||
synchronized (readLock) {
|
||||
synchronized (writeLock) {
|
||||
base.close();
|
||||
sockProxy = null;
|
||||
base.setProxy(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private native void socketConnect(byte[] addr, String hostname, int port)
|
||||
@ -620,12 +674,6 @@ public class SSLSocket extends java.net.Socket {
|
||||
setSSLDefaultOption(SocketBase.SSL_NO_CACHE, !b);
|
||||
}
|
||||
|
||||
private InetAddress inetAddress;
|
||||
private int port;
|
||||
private SocketProxy sockProxy;
|
||||
private boolean open = false;
|
||||
private boolean handshakeAsClient=true;
|
||||
private SocketBase base = new SocketBase();
|
||||
|
||||
private static void setSSLDefaultOption(int option, boolean on)
|
||||
throws SocketException
|
||||
@ -663,11 +711,49 @@ public class SSLSocket extends java.net.Socket {
|
||||
throws IOException;
|
||||
|
||||
int read(byte[] b, int off, int len) throws IOException {
|
||||
return socketRead(b, off, len, base.getTimeout());
|
||||
synchronized (readLock) {
|
||||
synchronized (this) {
|
||||
if ( isClosed ) { /* abort read if socket is closed */
|
||||
throw new IOException(
|
||||
"Socket has been closed, and cannot be reused.");
|
||||
}
|
||||
inRead = true;
|
||||
}
|
||||
int iRet;
|
||||
try {
|
||||
iRet = socketRead(b, off, len, base.getTimeout());
|
||||
} catch (IOException ioe) {
|
||||
throw new IOException(
|
||||
"SocketException cannot read on socket");
|
||||
} finally {
|
||||
synchronized (this) {
|
||||
inRead = false;
|
||||
}
|
||||
}
|
||||
return iRet;
|
||||
}
|
||||
}
|
||||
|
||||
void write(byte[] b, int off, int len) throws IOException {
|
||||
socketWrite(b, off, len, base.getTimeout());
|
||||
synchronized (writeLock) {
|
||||
synchronized (this) {
|
||||
if ( isClosed ) { /* abort write if socket is closed */
|
||||
throw new IOException(
|
||||
"Socket has been closed, and cannot be reused.");
|
||||
}
|
||||
inWrite = true;
|
||||
}
|
||||
try {
|
||||
socketWrite(b, off, len, base.getTimeout());
|
||||
} catch (IOException ioe) {
|
||||
throw new IOException(
|
||||
"SocketException cannot write on socket");
|
||||
} finally {
|
||||
synchronized (this) {
|
||||
inWrite = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private native int socketRead(byte[] b, int off, int len, int timeout)
|
||||
@ -701,7 +787,9 @@ public class SSLSocket extends java.net.Socket {
|
||||
*/
|
||||
public native void redoHandshake(boolean flushCache) throws SocketException;
|
||||
|
||||
protected void finalize() throws Throwable { }
|
||||
protected void finalize() throws Throwable {
|
||||
close(); /* in case user did not call close */
|
||||
}
|
||||
|
||||
public static class CipherPolicy {
|
||||
private int _enum;
|
||||
|
@ -107,9 +107,10 @@ class SocketBase {
|
||||
socketClose();
|
||||
}
|
||||
|
||||
// This method is synchronized because there is a potential race
|
||||
// condition in the native code.
|
||||
native synchronized void socketClose() throws IOException;
|
||||
// SSLServerSocket and SSLSocket close methods
|
||||
// have their own synchronization control that
|
||||
// protects SocketBase.socketClose.
|
||||
native void socketClose() throws IOException;
|
||||
|
||||
private boolean requestingClientAuth = false;
|
||||
|
||||
|
@ -149,9 +149,10 @@ Java_org_mozilla_jss_ssl_SocketBase_socketCreate(JNIEnv *env, jobject self,
|
||||
jobject clientCertSelectionCallback, jobject javaSock, jstring host)
|
||||
{
|
||||
jbyteArray sdArray = NULL;
|
||||
JSSL_SocketData *sockdata;
|
||||
JSSL_SocketData *sockdata = NULL;
|
||||
SECStatus status;
|
||||
PRFileDesc *newFD;
|
||||
PRFileDesc *tmpFD;
|
||||
PRFilePrivate *priv = NULL;
|
||||
|
||||
if( javaSock == NULL ) {
|
||||
@ -173,16 +174,18 @@ Java_org_mozilla_jss_ssl_SocketBase_socketCreate(JNIEnv *env, jobject self,
|
||||
}
|
||||
|
||||
/* enable SSL on the socket */
|
||||
newFD = SSL_ImportFD(NULL, newFD);
|
||||
if( newFD == NULL ) {
|
||||
tmpFD = SSL_ImportFD(NULL, newFD);
|
||||
if( tmpFD == NULL ) {
|
||||
JSSL_throwSSLSocketException(env, "SSL_ImportFD() returned NULL");
|
||||
goto finish;
|
||||
}
|
||||
newFD = tmpFD;
|
||||
|
||||
sockdata = JSSL_CreateSocketData(env, sockObj, newFD, priv);
|
||||
if( sockdata == NULL ) {
|
||||
goto finish;
|
||||
}
|
||||
newFD = NULL;
|
||||
|
||||
if( host != NULL ) {
|
||||
const char *chars;
|
||||
@ -266,6 +269,9 @@ finish:
|
||||
if( sockdata != NULL ) {
|
||||
JSSL_DestroySocketData(env, sockdata);
|
||||
}
|
||||
if( newFD != NULL ) {
|
||||
PR_Close(newFD);
|
||||
}
|
||||
} else {
|
||||
PR_ASSERT( sdArray != NULL );
|
||||
}
|
||||
@ -280,6 +286,10 @@ JSSL_CreateSocketData(JNIEnv *env, jobject sockObj, PRFileDesc* newFD,
|
||||
|
||||
/* make a JSSL_SocketData structure */
|
||||
sockdata = PR_Malloc( sizeof(JSSL_SocketData) );
|
||||
if( sockdata == NULL ) {
|
||||
JSS_throw(env, OUT_OF_MEMORY_ERROR);
|
||||
goto finish;
|
||||
}
|
||||
sockdata->fd = newFD;
|
||||
sockdata->socketObject = NULL;
|
||||
sockdata->certApprovalCallback = NULL;
|
||||
@ -287,7 +297,16 @@ JSSL_CreateSocketData(JNIEnv *env, jobject sockObj, PRFileDesc* newFD,
|
||||
sockdata->clientCert = NULL;
|
||||
sockdata->clientCertSlot = NULL;
|
||||
sockdata->jsockPriv = priv;
|
||||
sockdata->closed = PR_FALSE;
|
||||
sockdata->lock = NULL;
|
||||
sockdata->reader = NULL;
|
||||
sockdata->writer = NULL;
|
||||
sockdata->accepter = NULL;
|
||||
|
||||
sockdata->lock = PR_NewLock();
|
||||
if( sockdata->lock == NULL ) {
|
||||
JSS_throw(env, OUT_OF_MEMORY_ERROR);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a global ref to the socket. Since it is a weak reference, it will
|
||||
@ -303,8 +322,6 @@ finish:
|
||||
if( sockdata != NULL ) {
|
||||
JSSL_DestroySocketData(env, sockdata);
|
||||
sockdata = NULL;
|
||||
} else {
|
||||
PR_ASSERT( sockdata != NULL );
|
||||
}
|
||||
}
|
||||
return sockdata;
|
||||
@ -314,18 +331,13 @@ JNIEXPORT void JNICALL
|
||||
Java_org_mozilla_jss_ssl_SocketProxy_releaseNativeResources
|
||||
(JNIEnv *env, jobject this)
|
||||
{
|
||||
JSSL_SocketData *sock = NULL;
|
||||
|
||||
/* get the FD */
|
||||
if( JSS_getPtrFromProxy(env, this, (void**)&sock) != PR_SUCCESS) {
|
||||
/* exception was thrown */
|
||||
goto finish;
|
||||
}
|
||||
|
||||
JSSL_DestroySocketData(env, sock);
|
||||
|
||||
finish:
|
||||
return;
|
||||
/* SSLSocket.close and SSLServerSocket.close call */
|
||||
/* SocketBase.close to destroy all native Resources */
|
||||
/* attached to the socket. There is no native resource */
|
||||
/* to release after close has been called. This method */
|
||||
/* remains because SocketProxy extends org.mozilla.jss.util.NativeProxy*/
|
||||
/* which defines releaseNativeResources as abstract and */
|
||||
/* therefore must be implemented by SocketProxy */
|
||||
}
|
||||
|
||||
void
|
||||
@ -333,11 +345,7 @@ JSSL_DestroySocketData(JNIEnv *env, JSSL_SocketData *sd)
|
||||
{
|
||||
PR_ASSERT(sd != NULL);
|
||||
|
||||
if( !sd->closed ) {
|
||||
PR_Close(sd->fd);
|
||||
sd->closed = PR_TRUE;
|
||||
/* this may have thrown an exception */
|
||||
}
|
||||
PR_Close(sd->fd);
|
||||
|
||||
if( sd->socketObject != NULL ) {
|
||||
DELETE_WEAK_GLOBAL_REF(env, sd->socketObject );
|
||||
@ -354,6 +362,9 @@ JSSL_DestroySocketData(JNIEnv *env, JSSL_SocketData *sd)
|
||||
if( sd->clientCertSlot != NULL ) {
|
||||
PK11_FreeSlot(sd->clientCertSlot);
|
||||
}
|
||||
if( sd->lock != NULL ) {
|
||||
PR_DestroyLock(sd->lock);
|
||||
}
|
||||
PR_Free(sd);
|
||||
}
|
||||
|
||||
@ -425,9 +436,8 @@ finish:
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is synchronized because of a potential race condition.
|
||||
* We want to avoid two threads simultaneously calling this code, in case
|
||||
* one sets sd->fd to NULL and then the other calls PR_Close on the NULL.
|
||||
* SSLServerSocket and SSLSocket have their own synchronization
|
||||
* that protects SocketBase.socketClose.
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_mozilla_jss_ssl_SocketBase_socketClose(JNIEnv *env, jobject self)
|
||||
@ -440,11 +450,7 @@ Java_org_mozilla_jss_ssl_SocketBase_socketClose(JNIEnv *env, jobject self)
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if( ! sock->closed ) {
|
||||
PR_Close(sock->fd);
|
||||
sock->closed = PR_TRUE;
|
||||
/* this may have thrown an exception */
|
||||
}
|
||||
JSSL_DestroySocketData(env, sock);
|
||||
|
||||
finish:
|
||||
EXCEPTION_CHECK(env, sock)
|
||||
|
@ -45,7 +45,10 @@ struct JSSL_SocketData {
|
||||
CERTCertificate *clientCert;
|
||||
PK11SlotInfo *clientCertSlot;
|
||||
PRFilePrivate *jsockPriv;
|
||||
PRBool closed;
|
||||
PRLock *lock; /* protects reader, writer, and accepter */
|
||||
PRThread *reader;
|
||||
PRThread *writer;
|
||||
PRThread *accepter;
|
||||
};
|
||||
typedef struct JSSL_SocketData JSSL_SocketData;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user