2810 lines
94 KiB
Java
2810 lines
94 KiB
Java
/* WolfSSLSocket.java
|
|
*
|
|
* Copyright (C) 2006-2024 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfSSL.
|
|
*
|
|
* wolfSSL is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfSSL is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
package com.wolfssl.provider.jsse;
|
|
|
|
import java.io.DataInputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.net.InetAddress;
|
|
import java.net.InetSocketAddress;
|
|
import java.net.Socket;
|
|
import java.net.SocketException;
|
|
import java.net.SocketAddress;
|
|
import java.net.SocketTimeoutException;
|
|
import java.util.ArrayList;
|
|
import java.util.function.BiFunction;
|
|
import java.util.List;
|
|
import java.util.Arrays;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.nio.channels.SocketChannel;
|
|
import java.security.cert.CertificateEncodingException;
|
|
|
|
import javax.net.ssl.HandshakeCompletedEvent;
|
|
import javax.net.ssl.HandshakeCompletedListener;
|
|
import javax.net.ssl.SSLHandshakeException;
|
|
import javax.net.ssl.SSLSession;
|
|
import javax.net.ssl.SSLSocket;
|
|
import javax.net.ssl.SSLParameters;
|
|
import javax.net.ssl.SSLException;
|
|
|
|
import com.wolfssl.WolfSSL;
|
|
import com.wolfssl.WolfSSLException;
|
|
import com.wolfssl.WolfSSLIOSendCallback;
|
|
import com.wolfssl.WolfSSLIORecvCallback;
|
|
import com.wolfssl.WolfSSLALPNSelectCallback;
|
|
import com.wolfssl.WolfSSLJNIException;
|
|
import com.wolfssl.WolfSSLSession;
|
|
|
|
|
|
/**
|
|
* wolfSSL implementation of SSLSocket
|
|
*
|
|
* @author wolfSSL
|
|
*/
|
|
public class WolfSSLSocket extends SSLSocket {
|
|
|
|
private WolfSSLAuthStore authStore = null;
|
|
|
|
/* WOLFSSL_CTX reference, passed down to this class */
|
|
private com.wolfssl.WolfSSLContext ctx = null;
|
|
|
|
/* WOLFSSL reference, created in this class */
|
|
private WolfSSLSession ssl = null;
|
|
private WolfSSLParameters params = null;
|
|
private WolfSSLEngineHelper EngineHelper = null;
|
|
|
|
private Socket socket = null;
|
|
private boolean autoClose;
|
|
|
|
private WolfSSLInputStream inStream;
|
|
private WolfSSLOutputStream outStream;
|
|
|
|
private ArrayList<HandshakeCompletedListener> hsListeners = null;
|
|
|
|
/** TLS handshake initialization called */
|
|
protected volatile boolean handshakeInitCalled = false;
|
|
/** TLS handshake has been started */
|
|
protected volatile boolean handshakeStarted = false;
|
|
/** TLS handshake has completed */
|
|
protected volatile boolean handshakeComplete = false;
|
|
/** Connection to peer has closed */
|
|
protected volatile boolean connectionClosed = false;
|
|
/** Flag representing if I/O callbacks have been set */
|
|
private boolean ioCallbacksSet = false;
|
|
/** Flag representing if native fd has been set */
|
|
private boolean fdSet = false;
|
|
|
|
/* lock for handshakInitCalled and handshakeComplete */
|
|
private final Object handshakeLock = new Object();
|
|
|
|
/* protect read/write/connect/accept from multiple threads simultaneously
|
|
* accessing WolfSSLSession object / WOLFSSL struct */
|
|
private final Object ioLock = new Object();
|
|
|
|
/* lock for get/set of SO timeout */
|
|
private final Object timeoutLock = new Object();
|
|
|
|
/* lock and status for WolfSSLSocket initialization */
|
|
private boolean isInitialized = false;
|
|
private final Object initLock = new Object();
|
|
|
|
/** ALPN selector callback, if set */
|
|
protected BiFunction<SSLSocket, List<String>, String> alpnSelector = null;
|
|
|
|
/**
|
|
* Create new WolfSSLSocket object
|
|
*
|
|
* @param context WolfSSLContext to use with this SSLSocket
|
|
* @param authStore WolfSSLAuthStore to use with this SSLSocket
|
|
* @param params WolfSSLParameters to use with this SSLSocket
|
|
* @param clientMode true if this is a client socket, otherwise false
|
|
*
|
|
* @throws IOException if initialization fails
|
|
*/
|
|
public WolfSSLSocket(com.wolfssl.WolfSSLContext context,
|
|
WolfSSLAuthStore authStore, WolfSSLParameters params,
|
|
boolean clientMode)
|
|
throws IOException {
|
|
|
|
super();
|
|
this.ctx = context;
|
|
this.authStore = authStore;
|
|
this.params = params.copy();
|
|
this.autoClose = true;
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"creating new WolfSSLSocket(clientMode: " +
|
|
String.valueOf(clientMode) + ")");
|
|
|
|
try {
|
|
initSSL();
|
|
|
|
EngineHelper = new WolfSSLEngineHelper(this.ssl, this.authStore,
|
|
this.params);
|
|
EngineHelper.setUseClientMode(clientMode);
|
|
|
|
} catch (WolfSSLException e) {
|
|
throw new IOException(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create new WolfSSLSocket object
|
|
*
|
|
* @param context WolfSSLContext to use with this SSLSocket
|
|
* @param authStore WolfSSLAuthStore to use with this SSLSocket
|
|
* @param params WolfSSLParameters to use with this SSLSocket
|
|
* @param clientMode true if this is a client socket, otherwise false
|
|
* @param host InetAddress of peer hostname
|
|
* @param port port of peer
|
|
*
|
|
* @throws IOException if initialization fails
|
|
*/
|
|
public WolfSSLSocket(com.wolfssl.WolfSSLContext context,
|
|
WolfSSLAuthStore authStore, WolfSSLParameters params,
|
|
boolean clientMode, InetAddress host, int port)
|
|
throws IOException {
|
|
|
|
super(host, port);
|
|
this.ctx = context;
|
|
this.authStore = authStore;
|
|
this.params = params.copy();
|
|
this.autoClose = true;
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"creating new WolfSSLSocket(clientMode: " +
|
|
String.valueOf(clientMode) + ", InetAddress, port: " +
|
|
port + ")");
|
|
|
|
try {
|
|
initSSL();
|
|
|
|
EngineHelper = new WolfSSLEngineHelper(this.ssl, this.authStore,
|
|
this.params, port, host);
|
|
EngineHelper.setUseClientMode(clientMode);
|
|
|
|
} catch (WolfSSLException e) {
|
|
throw new IOException(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create new WolfSSLSocket object
|
|
*
|
|
* @param context WolfSSLContext to use with this SSLSocket
|
|
* @param authStore WolfSSLAuthStore to use with this SSLSocket
|
|
* @param params WolfSSLParameters to use with this SSLSocket
|
|
* @param clientMode true if this is a client socket, otherwise false
|
|
* @param address InetAddress of peer hostname
|
|
* @param port port of peer
|
|
* @param localAddress local InetAddress to use for SSLSocket
|
|
* @param localPort local port to use for SSLSocket
|
|
*
|
|
* @throws IOException if initialization fails
|
|
*/
|
|
public WolfSSLSocket(com.wolfssl.WolfSSLContext context,
|
|
WolfSSLAuthStore authStore, WolfSSLParameters params,
|
|
boolean clientMode, InetAddress address, int port,
|
|
InetAddress localAddress, int localPort)
|
|
throws IOException {
|
|
|
|
super(address, port, localAddress, localPort);
|
|
this.ctx = context;
|
|
this.authStore = authStore;
|
|
this.params = params.copy();
|
|
this.autoClose = true;
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"creating new WolfSSLSocket(clientMode: " +
|
|
String.valueOf(clientMode) + ", InetAddress, port: " +
|
|
port + ", InetAddress, localPort: " + localPort + ")");
|
|
|
|
try {
|
|
initSSL();
|
|
|
|
EngineHelper = new WolfSSLEngineHelper(this.ssl, this.authStore,
|
|
this.params, port, address);
|
|
EngineHelper.setUseClientMode(clientMode);
|
|
|
|
} catch (WolfSSLException e) {
|
|
throw new IOException(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create new WolfSSLSocket object
|
|
*
|
|
* @param context WolfSSLContext to use with this SSLSocket
|
|
* @param authStore WolfSSLAuthStore to use with this SSLSocket
|
|
* @param params WolfSSLParameters to use with this SSLSocket
|
|
* @param clientMode true if this is a client socket, otherwise false
|
|
* @param host String of peer hostname
|
|
* @param port port of peer
|
|
*
|
|
* @throws IOException if initialization fails
|
|
*/
|
|
public WolfSSLSocket(com.wolfssl.WolfSSLContext context,
|
|
WolfSSLAuthStore authStore, WolfSSLParameters params,
|
|
boolean clientMode, String host, int port)
|
|
throws IOException {
|
|
|
|
super(host, port);
|
|
this.ctx = context;
|
|
this.authStore = authStore;
|
|
this.params = params.copy();
|
|
this.autoClose = true;
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"creating new WolfSSLSocket(clientMode: " +
|
|
String.valueOf(clientMode) + ", host: " + host + ", port: " +
|
|
port + ")");
|
|
|
|
try {
|
|
initSSL();
|
|
|
|
EngineHelper = new WolfSSLEngineHelper(this.ssl, this.authStore,
|
|
this.params, port, host);
|
|
EngineHelper.setUseClientMode(clientMode);
|
|
|
|
} catch (WolfSSLException e) {
|
|
throw new IOException(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create new WolfSSLSocket object
|
|
*
|
|
* @param context WolfSSLContext to use with this SSLSocket
|
|
* @param authStore WolfSSLAuthStore to use with this SSLSocket
|
|
* @param params WolfSSLParameters to use with this SSLSocket
|
|
* @param clientMode true if this is a client socket, otherwise false
|
|
* @param host InetAddress of peer hostname
|
|
* @param port port of peer
|
|
* @param localHost local InetAddress to use for SSLSocket
|
|
* @param localPort local port to use for SSLSocket
|
|
*
|
|
* @throws IOException if initialization fails
|
|
*/
|
|
public WolfSSLSocket(com.wolfssl.WolfSSLContext context,
|
|
WolfSSLAuthStore authStore, WolfSSLParameters params,
|
|
boolean clientMode, String host, int port, InetAddress localHost,
|
|
int localPort)
|
|
throws IOException {
|
|
|
|
super(host, port, localHost, localPort);
|
|
this.ctx = context;
|
|
this.authStore = authStore;
|
|
this.params = params.copy();
|
|
this.autoClose = true;
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"creating new WolfSSLSocket(clientMode: " +
|
|
String.valueOf(clientMode) + ", host: " + host + ", port: " +
|
|
port + ", InetAddress, locaPort: " + localPort + ")");
|
|
|
|
try {
|
|
initSSL();
|
|
|
|
EngineHelper = new WolfSSLEngineHelper(this.ssl, this.authStore,
|
|
this.params, port, host);
|
|
EngineHelper.setUseClientMode(clientMode);
|
|
|
|
} catch (WolfSSLException e) {
|
|
throw new IOException(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create new WolfSSLSocket object layered over an existing Socket
|
|
* connected to the named host, at the given port.
|
|
*
|
|
* host/port refer to logical peer, but Socket could be connected to
|
|
* a proxy.
|
|
*
|
|
* @param context WolfSSLContext to use with this SSLSocket
|
|
* @param authStore WolfSSLAuthStore to use with this SSLSocket
|
|
* @param params WolfSSLParameters to use with this SSLSocket
|
|
* @param clientMode true if this is a client socket, otherwise false
|
|
* @param s existing connected Socket
|
|
* @param host String with peer hostname
|
|
* @param port port of peer
|
|
* @param autoClose automatically close wrapped Socket when finished
|
|
*
|
|
* @throws IOException if initialization fails
|
|
*/
|
|
public WolfSSLSocket(com.wolfssl.WolfSSLContext context,
|
|
WolfSSLAuthStore authStore, WolfSSLParameters params,
|
|
boolean clientMode, Socket s, String host, int port,
|
|
boolean autoClose) throws IOException {
|
|
|
|
super();
|
|
this.ctx = context;
|
|
this.authStore = authStore;
|
|
this.params = params.copy();
|
|
this.socket = s;
|
|
this.autoClose = autoClose;
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"creating new WolfSSLSocket(clientMode: " +
|
|
String.valueOf(clientMode) + ", Socket, host: " + host +
|
|
", port: " + port + ", autoClose: " +
|
|
String.valueOf(autoClose) + ")");
|
|
|
|
if (s == null) {
|
|
throw new NullPointerException("Socket is null");
|
|
}
|
|
|
|
/* socket should already be connected */
|
|
if (!s.isConnected()) {
|
|
throw new IOException("Socket is not connected");
|
|
}
|
|
|
|
try {
|
|
initSSL();
|
|
|
|
EngineHelper = new WolfSSLEngineHelper(this.ssl, this.authStore,
|
|
this.params, port, host);
|
|
EngineHelper.setUseClientMode(clientMode);
|
|
|
|
} catch (WolfSSLException e) {
|
|
throw new IOException(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create new WolfSSLSocket object layered over an existing Socket.
|
|
*
|
|
* @param context WolfSSLContext to use with this SSLSocket
|
|
* @param authStore WolfSSLAuthStore to use with this SSLSocket
|
|
* @param params WolfSSLParameters to use with this SSLSocket
|
|
* @param clientMode true if this is a client socket, otherwise false
|
|
* @param s existing connected Socket
|
|
* @param autoClose automatically close wrapped Socket when finished
|
|
*
|
|
* @throws IOException if initialization fails
|
|
*/
|
|
public WolfSSLSocket(com.wolfssl.WolfSSLContext context,
|
|
WolfSSLAuthStore authStore, WolfSSLParameters params,
|
|
boolean clientMode, Socket s, boolean autoClose)
|
|
throws IOException {
|
|
|
|
super();
|
|
this.ctx = context;
|
|
this.authStore = authStore;
|
|
this.params = params.copy();
|
|
this.socket = s;
|
|
this.autoClose = autoClose;
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"creating new WolfSSLSocket(clientMode: " +
|
|
String.valueOf(clientMode) + ", Socket, autoClose: " +
|
|
String.valueOf(autoClose) + ")");
|
|
|
|
if (!s.isConnected()) {
|
|
throw new IOException("Socket is not connected");
|
|
}
|
|
|
|
try {
|
|
initSSL();
|
|
|
|
EngineHelper = new WolfSSLEngineHelper(this.ssl, this.authStore,
|
|
this.params, s.getPort(), s.getInetAddress());
|
|
EngineHelper.setUseClientMode(clientMode);
|
|
|
|
} catch (WolfSSLException e) {
|
|
throw new IOException(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create new WolfSSLSocket object layered over an existing Socket,
|
|
* only a server mode Socket. Use pre-consumed InputStream data
|
|
* if provided.
|
|
*
|
|
* @param context WolfSSLContext to use with this SSLSocket
|
|
* @param authStore WolfSSLAuthStore to use with this SSLSocket
|
|
* @param params WolfSSLParameters to use with this SSLSocket
|
|
* @param s existing connected Socket
|
|
* @param consumed pre-consumed Socket data to use for this SSLSocket
|
|
* @param autoClose automatically close wrapped Socket when finished
|
|
*
|
|
* @throws IOException if initialization fails
|
|
*/
|
|
public WolfSSLSocket(com.wolfssl.WolfSSLContext context,
|
|
WolfSSLAuthStore authStore, WolfSSLParameters params, Socket s,
|
|
InputStream consumed, boolean autoClose) throws IOException {
|
|
|
|
super();
|
|
this.ctx = context;
|
|
this.authStore = authStore;
|
|
this.params = params.copy();
|
|
this.socket = s;
|
|
this.autoClose = autoClose;
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"creating new WolfSSLSocket(Socket, InputStream, autoClose: " +
|
|
String.valueOf(autoClose) + ")");
|
|
|
|
if (s == null ) {
|
|
throw new NullPointerException("Socket is null");
|
|
}
|
|
|
|
if (!s.isConnected()) {
|
|
throw new IOException("Socket is not connected");
|
|
}
|
|
|
|
try {
|
|
initSSL();
|
|
|
|
EngineHelper = new WolfSSLEngineHelper(this.ssl, this.authStore,
|
|
this.params, s.getPort(), s.getInetAddress());
|
|
EngineHelper.setUseClientMode(false);
|
|
|
|
/* register custom receive callback to read consumed first */
|
|
if (consumed != null) {
|
|
ConsumedRecvCallback recvCb = new ConsumedRecvCallback();
|
|
this.ssl.setIORecv(recvCb);
|
|
ConsumedRecvCtx recvCtx = new ConsumedRecvCtx(s, consumed);
|
|
this.ssl.setIOReadCtx(recvCtx);
|
|
this.ioCallbacksSet = true;
|
|
}
|
|
|
|
} catch (WolfSSLException | WolfSSLJNIException e) {
|
|
throw new IOException(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create new internal WolfSSLSession object for use with this SSLSocket.
|
|
*
|
|
* @throws WolfSSLException on error creating WolfSSLSession
|
|
*/
|
|
private void initSSL() throws WolfSSLException {
|
|
|
|
/* Initialize WolfSSLSession object, wraps WOLFSSL structure. */
|
|
ssl = new WolfSSLSession(ctx);
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"created new native WOLFSSL");
|
|
}
|
|
|
|
/**
|
|
* Initialize this WolfSSLSocket.
|
|
*
|
|
* Internal method, should be called before any handshake, I/O, or
|
|
* other operations are conducted that would rely on a set up key/cert,
|
|
* file descriptor, or I/O callback.
|
|
*
|
|
* This logic is not included directly in WolfSSLSocket constructors
|
|
* to avoid possible 'this' escape before subclass is fully initialized
|
|
* when using 'this' from setFd().
|
|
*
|
|
* @throws IOException if initialization fails
|
|
*/
|
|
private void checkAndInitSSLSocket() throws IOException {
|
|
|
|
synchronized (initLock) {
|
|
|
|
/* If underlying Socket connected, set fd. Check before
|
|
* initialized flag, since we may have already initialized
|
|
* certs/keys but not fd in previous call */
|
|
if (!this.fdSet && isConnected()) {
|
|
try {
|
|
setFd();
|
|
} catch (WolfSSLException e) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"Failed to set native fd, may try again later");
|
|
}
|
|
}
|
|
|
|
if (isInitialized) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
/* Load private key and cert chain from WolfSSLAuthStore */
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"loading private key and cert chain");
|
|
|
|
if (this.socket != null) {
|
|
EngineHelper.LoadKeyAndCertChain(this.socket, null);
|
|
} else {
|
|
EngineHelper.LoadKeyAndCertChain(this, null);
|
|
}
|
|
|
|
isInitialized = true;
|
|
|
|
} catch (WolfSSLException | CertificateEncodingException |
|
|
IOException e) {
|
|
throw new IOException(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register I/O callbacks with native wolfSSL which use
|
|
* Input/OutputStream of the wrapped Socket object.
|
|
*
|
|
* Called by setFd() if ssl.setFd() fails to find or set the internal
|
|
* SocketImpl file descriptor.
|
|
*
|
|
* @throws WolfSSLException if this.socket is null or setting I/O
|
|
* callbacks or ctx fails
|
|
*/
|
|
private void setIOCallbacks() throws WolfSSLException {
|
|
|
|
if (this.socket == null) {
|
|
throw new WolfSSLException(
|
|
"Internal Socket is null, unable to set I/O callbacks");
|
|
}
|
|
|
|
if (this.ioCallbacksSet) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"wolfSSL I/O callbacks already set, skipping");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
/* Register send callback and context */
|
|
SocketSendCallback sendCb = new SocketSendCallback();
|
|
this.ssl.setIOSend(sendCb);
|
|
SocketSendCtx writeCtx = new SocketSendCtx(this.socket);
|
|
this.ssl.setIOWriteCtx(writeCtx);
|
|
|
|
/* Register recv callback and context */
|
|
SocketRecvCallback recvCb = new SocketRecvCallback();
|
|
this.ssl.setIORecv(recvCb);
|
|
SocketRecvCtx readCtx = new SocketRecvCtx(this.socket);
|
|
this.ssl.setIOReadCtx(readCtx);
|
|
|
|
} catch (WolfSSLJNIException e) {
|
|
throw new WolfSSLException(e);
|
|
}
|
|
}
|
|
|
|
private void setFd() throws IllegalArgumentException, WolfSSLException {
|
|
|
|
int ret;
|
|
|
|
if (ssl == null) {
|
|
throw new IllegalArgumentException("WolfSSLSession object is null");
|
|
}
|
|
|
|
/* Synchronized on ioLock to prevent read/write/connect/accept calls
|
|
* from possibly being called before descriptor or I/O callbacks
|
|
* have been set */
|
|
synchronized (ioLock) {
|
|
if (this.socket == null) {
|
|
ret = ssl.setFd(this);
|
|
if (ret != WolfSSL.SSL_SUCCESS) {
|
|
throw new WolfSSLException(
|
|
"Failed to set native Socket fd");
|
|
}
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"registered SSLSocket(this) with native wolfSSL");
|
|
|
|
} else {
|
|
ret = ssl.setFd(this.socket);
|
|
if (ret != WolfSSL.SSL_SUCCESS) {
|
|
/* Failed to find/set internal SocketImpl file descriptor.
|
|
* Try using I/O callbacks instead with
|
|
* Input/OutputStream */
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"Failed to set native SocketImpl fd, " +
|
|
"trying I/O callbacks");
|
|
|
|
setIOCallbacks();
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"registered underlying Socket with " +
|
|
"wolfSSL I/O callbacks");
|
|
}
|
|
else {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"registered Socket(this.socket) with native wolfSSL");
|
|
}
|
|
}
|
|
|
|
/* Mark fd set */
|
|
this.fdSet = true;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns unique SocketChannel object assiciated with this socket.
|
|
*/
|
|
@Override
|
|
public final SocketChannel getChannel() {
|
|
if (this.socket != null) {
|
|
return this.socket.getChannel();
|
|
} else {
|
|
return super.getChannel();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the address of the remote peer.
|
|
*/
|
|
@Override
|
|
public final InetAddress getInetAddress() {
|
|
if (this.socket != null) {
|
|
return this.socket.getInetAddress();
|
|
} else {
|
|
return super.getInetAddress();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the local address the socket is bound to.
|
|
*/
|
|
@Override
|
|
public final InetAddress getLocalAddress() {
|
|
if (this.socket != null) {
|
|
return this.socket.getLocalAddress();
|
|
} else {
|
|
return super.getLocalAddress();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get remote port number used by this socket.
|
|
*/
|
|
@Override
|
|
public final int getPort() {
|
|
if (this.socket != null) {
|
|
return this.socket.getPort();
|
|
} else {
|
|
return super.getPort();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get local port number used by this socket.
|
|
*/
|
|
@Override
|
|
public final int getLocalPort() {
|
|
if (this.socket != null) {
|
|
return this.socket.getLocalPort();
|
|
} else {
|
|
return super.getLocalPort();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tests if SO_KEEPALIVE is enabled on this socket.
|
|
*/
|
|
@Override
|
|
public final boolean getKeepAlive() throws SocketException {
|
|
if (this.socket != null) {
|
|
return this.socket.getKeepAlive();
|
|
} else {
|
|
return super.getKeepAlive();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tests if SO_REUSEADDR is enabled on this socket.
|
|
*/
|
|
@Override
|
|
public final boolean getReuseAddress() throws SocketException {
|
|
if (this.socket != null) {
|
|
return this.socket.getReuseAddress();
|
|
} else {
|
|
return super.getReuseAddress();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return address of the endpoint that this socket is bound to,
|
|
* or null if not bound yet.
|
|
*/
|
|
@Override
|
|
public final SocketAddress getLocalSocketAddress() {
|
|
if (this.socket != null) {
|
|
return this.socket.getLocalSocketAddress();
|
|
} else {
|
|
return super.getLocalSocketAddress();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tests if OOBINLINE is enabled.
|
|
*/
|
|
@Override
|
|
public final boolean getOOBInline() throws SocketException {
|
|
if (this.socket != null) {
|
|
return this.socket.getOOBInline();
|
|
} else {
|
|
return super.getOOBInline();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the value of the SO_RCVBUF option for this socket.
|
|
*/
|
|
@Override
|
|
public final synchronized int getReceiveBufferSize()
|
|
throws SocketException {
|
|
if (this.socket != null) {
|
|
return this.socket.getReceiveBufferSize();
|
|
} else {
|
|
return super.getReceiveBufferSize();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the address of the remote endpoint, or null if not connected.
|
|
*/
|
|
@Override
|
|
public final SocketAddress getRemoteSocketAddress() {
|
|
if (this.socket != null) {
|
|
return this.socket.getRemoteSocketAddress();
|
|
} else {
|
|
return super.getRemoteSocketAddress();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the value of the SO_SNDBUF option for this socket.
|
|
*/
|
|
@Override
|
|
public final synchronized int getSendBufferSize()
|
|
throws SocketException {
|
|
if (this.socket != null) {
|
|
return this.socket.getSendBufferSize();
|
|
} else {
|
|
return super.getSendBufferSize();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the value of the SO_SNDBUF option for this socket. This setting
|
|
* only affects socket close.
|
|
* @return -1 if option is disabled, otherwise int value
|
|
*/
|
|
@Override
|
|
public final int getSoLinger() throws SocketException {
|
|
if (this.socket != null) {
|
|
return this.socket.getSoLinger();
|
|
} else {
|
|
return super.getSoLinger();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tests if TCP_NODELAY is enabled.
|
|
*/
|
|
@Override
|
|
public final boolean getTcpNoDelay() throws SocketException {
|
|
if (this.socket != null) {
|
|
return this.socket.getTcpNoDelay();
|
|
} else {
|
|
return super.getTcpNoDelay();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets traffic class or type of service in IP header.
|
|
*/
|
|
@Override
|
|
public final int getTrafficClass() throws SocketException {
|
|
if (this.socket != null) {
|
|
return this.socket.getTrafficClass();
|
|
} else {
|
|
return super.getTrafficClass();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the binding state for this socket.
|
|
*/
|
|
@Override
|
|
public final boolean isBound() {
|
|
if (this.socket != null) {
|
|
return this.socket.isBound();
|
|
} else {
|
|
return super.isBound();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the closed state of the socket.
|
|
*/
|
|
@Override
|
|
public final boolean isClosed() {
|
|
if (this.socket != null) {
|
|
return this.socket.isClosed();
|
|
} else {
|
|
return super.isClosed();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the connection state of this socket.
|
|
*/
|
|
@Override
|
|
public final boolean isConnected() {
|
|
if (this.socket != null) {
|
|
return this.socket.isConnected();
|
|
} else {
|
|
return super.isConnected();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns whether the read-half of the socket connection is closed.
|
|
*/
|
|
@Override
|
|
public final boolean isInputShutdown() {
|
|
if (this.socket != null) {
|
|
return this.socket.isInputShutdown();
|
|
} else {
|
|
return super.isInputShutdown();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns whether the write-half of the socket connection is closed.
|
|
*/
|
|
@Override
|
|
public final boolean isOutputShutdown() {
|
|
if (this.socket != null) {
|
|
return this.socket.isOutputShutdown();
|
|
} else {
|
|
return super.isOutputShutdown();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send one byte of urgent data on the socket.
|
|
* Not supported by SSLSockets at this point.
|
|
*/
|
|
@Override
|
|
public final void sendUrgentData(int data) throws IOException {
|
|
throw new SocketException("sendUrgentData() not supported by "
|
|
+ "WolfSSLSocket");
|
|
}
|
|
|
|
/**
|
|
* Enable/disable SO_KEEPALIVE.
|
|
*/
|
|
@Override
|
|
public final void setKeepAlive(boolean on) throws SocketException {
|
|
if (this.socket != null) {
|
|
this.socket.setKeepAlive(on);
|
|
} else {
|
|
super.setKeepAlive(on);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enable/disable SO_KEEPALIVE.
|
|
* Enable/disable OOBINLINE (receipt of TCP urgent data). This option
|
|
* is disabled by default. Setting OOBInline does not have any effect
|
|
* on WolfSSLSocket since SSLSocket does not support sending urgent data.
|
|
*/
|
|
@Override
|
|
public final void setOOBInline(boolean on) throws SocketException {
|
|
throw new SocketException("setOOBInline is ineffective, as sending " +
|
|
"urgent data is not supported with SSLSocket");
|
|
}
|
|
|
|
/**
|
|
* Set performance preferences for this socket.
|
|
*/
|
|
@Override
|
|
public final void setPerformancePreferences(int connectionTime,
|
|
int latency, int bandwidth) {
|
|
if (this.socket != null) {
|
|
this.socket.setPerformancePreferences(connectionTime,
|
|
latency, bandwidth);
|
|
} else {
|
|
super.setPerformancePreferences(connectionTime, latency,
|
|
bandwidth);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the SO_RCVBUF option to the specified value for this Socket.
|
|
*/
|
|
@Override
|
|
public final synchronized void setReceiveBufferSize(int size)
|
|
throws SocketException {
|
|
if (this.socket != null) {
|
|
this.socket.setReceiveBufferSize(size);
|
|
} else {
|
|
super.setReceiveBufferSize(size);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enable/disable the SO_REUSEADDR socket option.
|
|
*/
|
|
@Override
|
|
public final void setReuseAddress(boolean on) throws SocketException {
|
|
if (this.socket != null) {
|
|
this.socket.setReuseAddress(on);
|
|
} else {
|
|
super.setReuseAddress(on);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the SO_SNDBUF option to the specified value for this Socket.
|
|
*/
|
|
@Override
|
|
public final synchronized void setSendBufferSize(int size)
|
|
throws SocketException {
|
|
if (this.socket != null) {
|
|
this.socket.setSendBufferSize(size);
|
|
} else {
|
|
super.setSendBufferSize(size);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enable/disable SO_LINGER with specified linger time in seconds.
|
|
*/
|
|
@Override
|
|
public final void setSoLinger(boolean on, int linger)
|
|
throws SocketException {
|
|
if (this.socket != null) {
|
|
this.socket.setSoLinger(on, linger);
|
|
} else {
|
|
super.setSoLinger(on, linger);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enable/disable TCP_NODELAY on this Socket.
|
|
*/
|
|
@Override
|
|
public final void setTcpNoDelay(boolean on) throws SocketException {
|
|
if (this.socket != null) {
|
|
this.socket.setTcpNoDelay(on);
|
|
} else {
|
|
super.setTcpNoDelay(on);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets traffic class or type-of-service octet in the IP header for packets
|
|
* sent from this Socket.
|
|
*/
|
|
@Override
|
|
public final void setTrafficClass(int tc) throws SocketException {
|
|
if (this.socket != null) {
|
|
this.socket.setTrafficClass(tc);
|
|
} else {
|
|
super.setTrafficClass(tc);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* shutdownInput() not supported with SSLSocket, matches OpenJDK behavior.
|
|
*/
|
|
@Override
|
|
public final void shutdownInput() throws IOException {
|
|
throw new UnsupportedOperationException("shutdownInput() not " +
|
|
"supported by wolfSSLSocket");
|
|
}
|
|
|
|
/**
|
|
* shutdownOutput() not supported with SSLSocket, matches OpenJDK behavior.
|
|
*/
|
|
@Override
|
|
public final void shutdownOutput() throws IOException {
|
|
throw new UnsupportedOperationException("shutdownOutput() not " +
|
|
"supported by wolfSSLSocket");
|
|
}
|
|
|
|
/**
|
|
* Returns the supported cipher suite list for this socket, and that
|
|
* have been compiled into native wolfSSL library.
|
|
*
|
|
* @return array of supported cipher suite Strings
|
|
*/
|
|
@Override
|
|
public synchronized String[] getSupportedCipherSuites() {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered getSupportedCipherSuites()");
|
|
|
|
return EngineHelper.getAllCiphers();
|
|
}
|
|
|
|
/**
|
|
* Returns array of enabled cipher suites for this Socket.
|
|
* This array is pre-populated by wolfJSSE with the cipher suites
|
|
* supported by the native wolfSSL library
|
|
|
|
* @return array of enabled cipher suite Strings
|
|
*/
|
|
@Override
|
|
public synchronized String[] getEnabledCipherSuites() {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered getEnabledCipherSuites()");
|
|
|
|
return EngineHelper.getCiphers();
|
|
}
|
|
|
|
/**
|
|
* Sets the cipher suites enabled for this SSLSocket.
|
|
*
|
|
* @param suites array of cipher suites to enable for this Socket
|
|
*
|
|
* @throws IllegalArgumentException when suites array contains
|
|
* cipher suites unsupported by native wolfSSL
|
|
*/
|
|
@Override
|
|
public synchronized void setEnabledCipherSuites(String[] suites)
|
|
throws IllegalArgumentException {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered setEnabledCipherSuites()");
|
|
|
|
/* sets cipher suite(s) to be used for connection */
|
|
EngineHelper.setCiphers(suites);
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"enabled cipher suites set to: " + Arrays.toString(suites));
|
|
}
|
|
|
|
/**
|
|
* Returns the most recent application protocol value negotiated
|
|
* during the SSL/TLS handshake by ALPN.
|
|
*
|
|
* Not marked at @Override since this API was added as of
|
|
* Java SE 8 Maintenance Release 3, and Java 7 SSLSocket will not
|
|
* have this.
|
|
*
|
|
* @return String representating the application protocol negotiated
|
|
*/
|
|
public synchronized String getApplicationProtocol() {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered getApplicationProtocol()");
|
|
|
|
return EngineHelper.getAlpnSelectedProtocolString();
|
|
}
|
|
|
|
/**
|
|
* Returns the application protocol value negotiated on a handshake
|
|
* currently in progress.
|
|
*
|
|
* After the handshake has finished, this will return null. To get the
|
|
* ALPN protocol negotiated during the handshake, after it has completed,
|
|
* call getApplicationProtocol().
|
|
*
|
|
* Not marked at @Override since this API was added as of
|
|
* Java SE 8 Maintenance Release 3, and Java 7 SSLSocket will not
|
|
* have this.
|
|
*
|
|
* @return String representating the application protocol negotiated
|
|
*/
|
|
public synchronized String getHandshakeApplicationProtocol() {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered getHandshakeApplicationProtocol()");
|
|
|
|
if (this.handshakeStarted && !this.handshakeComplete) {
|
|
return EngineHelper.getAlpnSelectedProtocolString();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Returns the callback that selects an application protocol during the
|
|
* SSL/TLS handshake.
|
|
*
|
|
* @return the callback function, or null if no callback has been set
|
|
*/
|
|
public synchronized BiFunction<SSLSocket,List<String>,String>
|
|
getHandshakeApplicationProtocolSelector() {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered getHandshakeApplicationProtocolSelector()");
|
|
|
|
return this.alpnSelector;
|
|
}
|
|
|
|
/**
|
|
* Registers a callback function that selects an application protocol
|
|
* value for the SSL/TLS handshake.
|
|
*
|
|
* Usage of this callback will override any values set by
|
|
* SSLParameters.setApplicationProtocols().
|
|
*
|
|
* Callback argument descriptions:
|
|
*
|
|
* SSLSocket - the current SSLSocket, allows for inspection by the
|
|
* callback if needed
|
|
* List<String> - List of Strings representing application protocol
|
|
* names sent by the peer
|
|
* String - Result of the callback is an application protocol
|
|
* name String, or null if none of the peer's protocols
|
|
* are acceptable. If return value is an empty String,
|
|
* ALPN will not be used.
|
|
*
|
|
* @param selector callback used to select ALPN protocol for handshake
|
|
*/
|
|
public synchronized void setHandshakeApplicationProtocolSelector(
|
|
BiFunction<SSLSocket,List<String>,String> selector) {
|
|
|
|
int ret = 0;
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered setHandshakeApplicationProtocolSelector()");
|
|
|
|
if (selector != null) {
|
|
ALPNSelectCallback alpnCb = new ALPNSelectCallback();
|
|
|
|
try {
|
|
/* Pass in SSLSocket Object for use inside callback */
|
|
ret = this.ssl.setAlpnSelectCb(alpnCb, this);
|
|
if (ret != WolfSSL.SSL_SUCCESS) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"Native setAlpnSelectCb() failed, ret = " + ret +
|
|
", not setting selector");
|
|
return;
|
|
}
|
|
|
|
/* called from within ALPNSelectCallback during the handshake */
|
|
this.alpnSelector = selector;
|
|
|
|
} catch (WolfSSLJNIException e) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"Exception while calling ssl.setAlpnSelectCb, not setting");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Inner class that implement the ALPN select callback which is registered
|
|
* with our com.wolfssl.WolfSSLSession when
|
|
* setHandshakeApplicationProtocolSelector() has been called.
|
|
*/
|
|
class ALPNSelectCallback implements WolfSSLALPNSelectCallback
|
|
{
|
|
public int alpnSelectCallback(WolfSSLSession ssl, String[] out,
|
|
String[] in, Object arg) {
|
|
|
|
SSLSocket sock = (SSLSocket)arg;
|
|
List<String> peerProtos = new ArrayList<String>();
|
|
String selected = null;
|
|
|
|
if (alpnSelector == null) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"alpnSelector null inside ALPNSelectCallback");
|
|
return WolfSSL.SSL_TLSEXT_ERR_ALERT_FATAL;
|
|
}
|
|
|
|
if (!(arg instanceof SSLSocket)) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"alpnSelectCallback arg not type of SSLSocket");
|
|
return WolfSSL.SSL_TLSEXT_ERR_ALERT_FATAL;
|
|
}
|
|
|
|
if (in.length == 0) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"peer protocol list is 0 inside alpnSelectCallback");
|
|
return WolfSSL.SSL_TLSEXT_ERR_ALERT_FATAL;
|
|
}
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"ALPN protos sent by peer: " + Arrays.toString(in));
|
|
|
|
for (String s: in) {
|
|
peerProtos.add(s);
|
|
}
|
|
selected = alpnSelector.apply(sock, peerProtos);
|
|
|
|
if (selected == null) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"ALPN protocol string is null, no peer match");
|
|
return WolfSSL.SSL_TLSEXT_ERR_ALERT_FATAL;
|
|
}
|
|
else {
|
|
if (selected.isEmpty()) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"ALPN not being used, selected proto empty");
|
|
return WolfSSL.SSL_TLSEXT_ERR_NOACK;
|
|
}
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"ALPN protocol selected by callback: " + selected);
|
|
out[0] = selected;
|
|
return WolfSSL.SSL_TLSEXT_ERR_OK;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns array of protocols supported by this SSLSocket.
|
|
*
|
|
* @return String array containing supported SSL/TLS protocols
|
|
*/
|
|
@Override
|
|
public synchronized String[] getSupportedProtocols() {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered getSupportedProtocols()");
|
|
|
|
/* returns all protocol version supported by native wolfSSL */
|
|
return EngineHelper.getAllProtocols();
|
|
}
|
|
|
|
/**
|
|
* Returns SSL/TLS protocols enabled for this SSLSocket.
|
|
*
|
|
* @return String array containing enabled protocols
|
|
*/
|
|
@Override
|
|
public synchronized String[] getEnabledProtocols() {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered getEnabledProtocols()");
|
|
|
|
/* returns protocols versions enabled for this session */
|
|
return EngineHelper.getProtocols();
|
|
}
|
|
|
|
/**
|
|
* Sets the SSL/TLS protocols enabled on this SSLSocket.
|
|
*
|
|
* @param protocols String array of SSL/TLS protocols to enable
|
|
*
|
|
* @throws IllegalArgumentException when protocols array contains
|
|
* protocols unsupported by native wolfSSL
|
|
*/
|
|
@Override
|
|
public synchronized void setEnabledProtocols(String[] protocols)
|
|
throws IllegalArgumentException {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered setEnabledProtocols()");
|
|
|
|
/* sets protocol versions to be enabled for use with this session */
|
|
EngineHelper.setProtocols(protocols);
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"enabled protocols set to: " + Arrays.toString(protocols));
|
|
}
|
|
|
|
/**
|
|
* Set ALPN extension protocol for this session.
|
|
* Calls native SSL_set_alpn_protos() at native level. Format starts with
|
|
* length, where length does not include length byte itself. Example format:
|
|
*
|
|
* Non-standard JSSE API, needed for Android compatibility. Some frameworks
|
|
* such as OkHttp expect this API to be here.
|
|
*
|
|
* byte[] p = "http/1.1".getBytes();
|
|
*
|
|
* @param alpnProtos ALPN protocols, encoded as byte array vector
|
|
*/
|
|
public synchronized void setAlpnProtocols(byte[] alpnProtos) {
|
|
|
|
/* store protocol array in WolfSSLParameters, will push to WOLFSSL
|
|
* from EngineHelper */
|
|
EngineHelper.setAlpnProtocols(alpnProtos);
|
|
}
|
|
|
|
/**
|
|
* Return ALPN protocol established for this session.
|
|
* Calls native SSL_get0_alpn_selected().
|
|
*
|
|
* Non-standard JSSE API, needed for Android compatibility. Some frameworks
|
|
* such as OkHttp expect this API to be here.
|
|
*
|
|
* @return byte array representation of selected protocol, starting with
|
|
* length byte. Length does not include length byte itself.
|
|
*/
|
|
public synchronized byte[] getAlpnSelectedProtocol() {
|
|
return EngineHelper.getAlpnSelectedProtocol();
|
|
}
|
|
|
|
/**
|
|
* Returns the SSLSession in use by this SSLSocket.
|
|
*
|
|
* @return SSLSession object, otherwise null if not handshaking or
|
|
* Socket has not progressed enough to create the session
|
|
*/
|
|
@Override
|
|
public synchronized SSLSession getSession() {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered getSession()");
|
|
|
|
try {
|
|
/* try to do handshake if not completed yet,
|
|
* handles synchronization */
|
|
if (this.handshakeComplete == false) {
|
|
this.startHandshake();
|
|
}
|
|
} catch (Exception e) {
|
|
/* Log error, but continue. Session returned will be empty */
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"Handshake attempt failed in SSLSocket.getSession()");
|
|
|
|
/* close SSLSocket */
|
|
try {
|
|
close();
|
|
} catch (Exception ex) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"close attempt failed in SSLSocket.getSession(): " + ex);
|
|
}
|
|
|
|
/* return invalid session object with cipher suite
|
|
* "SSL_NULL_WITH_NULL_NULL" */
|
|
return new WolfSSLImplementSSLSession(this.authStore);
|
|
}
|
|
|
|
return EngineHelper.getSession();
|
|
}
|
|
|
|
/**
|
|
* Returns the SSLSession being constructed during the SSL/TLS handshake.
|
|
*
|
|
* Unlike SSLSocket.getSession(), this does not start the handshake
|
|
* automatically if it has not been done yet.
|
|
*
|
|
* @return null if not handshaking yet or handshake is not far enough
|
|
* to have a SSLSession. Otherwise, returns the SSLSession
|
|
* being negotiated with peer.
|
|
*/
|
|
@Override
|
|
public synchronized SSLSession getHandshakeSession() {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered getHandshakeSession()");
|
|
|
|
if (this.handshakeStarted == false) {
|
|
return null;
|
|
}
|
|
|
|
return EngineHelper.getSession();
|
|
}
|
|
|
|
/**
|
|
* Registers a HandshakeCompletedListener with this SSLSocket.
|
|
*
|
|
* The handshake completed listener will be notified when the SSL/TLS
|
|
* handshake on this Socket has completed.
|
|
*
|
|
* @param listener the handshake listener to register
|
|
*
|
|
* @throws IllegalArgumentException when listener is null
|
|
*/
|
|
@Override
|
|
public synchronized void addHandshakeCompletedListener(
|
|
HandshakeCompletedListener listener) throws IllegalArgumentException {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered addHandshakeCompletedListener()");
|
|
|
|
if (listener == null) {
|
|
throw new IllegalArgumentException("HandshakeCompletedListener " +
|
|
"is null");
|
|
}
|
|
|
|
if (hsListeners == null) {
|
|
hsListeners = new ArrayList<HandshakeCompletedListener>();
|
|
}
|
|
|
|
hsListeners.add(listener);
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"registered new HandshakeCompletedListener");
|
|
}
|
|
|
|
/**
|
|
* Removes a registered HandshakeCompletedListener from this SSLSocket.
|
|
*
|
|
* @param listener the listener to be removed
|
|
*
|
|
* @throws IllegalArgumentException if listener is null, or has not
|
|
* been registered wit this Socket
|
|
*/
|
|
@Override
|
|
public synchronized void removeHandshakeCompletedListener(
|
|
HandshakeCompletedListener listener) throws IllegalArgumentException {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered removeHandshakeCompletedListener()");
|
|
|
|
if (listener == null) {
|
|
throw new IllegalArgumentException("HandshakeCompletedListener " +
|
|
"is null");
|
|
}
|
|
|
|
if (hsListeners != null) {
|
|
boolean removed = hsListeners.remove(listener);
|
|
if (removed == false) {
|
|
throw new IllegalArgumentException(
|
|
"HandshakeCompletedListener not a registered listener");
|
|
}
|
|
}
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"removed HandshakeCompletedListener");
|
|
}
|
|
|
|
/**
|
|
* Begins the SSL/TLS handshake on this SSLSocket.
|
|
*
|
|
* @throws IOException if a network error occurs
|
|
*/
|
|
@Override
|
|
public synchronized void startHandshake() throws IOException {
|
|
int ret;
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered startHandshake(), trying to get handshakeLock");
|
|
|
|
checkAndInitSSLSocket();
|
|
|
|
synchronized (handshakeLock) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"thread got handshakeLock (initHandshake)");
|
|
|
|
if (!this.isConnected()) {
|
|
throw new SocketException("Socket is not connected");
|
|
}
|
|
|
|
if (connectionClosed == true) {
|
|
throw new SocketException("Connection already shutdown");
|
|
}
|
|
|
|
if (handshakeComplete == true && getSession().isValid()) {
|
|
/* Handshake already finished:
|
|
* - Return early if session still valid.
|
|
* - Otherwise proceed with new handshake. */
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"handshake already finished, returning early");
|
|
return;
|
|
}
|
|
|
|
if (handshakeInitCalled == false) {
|
|
/* will throw SSLHandshakeException if session creation is
|
|
not allowed */
|
|
EngineHelper.initHandshake(this);
|
|
handshakeInitCalled = true;
|
|
}
|
|
|
|
/* Mark handshake as started */
|
|
this.handshakeStarted = true;
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"thread exiting handshakeLock (initHandshake)");
|
|
}
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"trying to get ioLock (handshake)");
|
|
|
|
synchronized (ioLock) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"thread got ioLock (handshake)");
|
|
|
|
try {
|
|
ret = EngineHelper.doHandshake(0, this.getSoTimeout());
|
|
} catch (SocketTimeoutException e) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"got socket timeout in doHandshake()");
|
|
/* close socket if the handshake is unsuccessful */
|
|
close();
|
|
throw e;
|
|
}
|
|
|
|
if (ret != WolfSSL.SSL_SUCCESS) {
|
|
int err = ssl.getError(ret);
|
|
String errStr = WolfSSL.getErrorString(err);
|
|
|
|
/* close socket if the handshake is unsuccessful */
|
|
close();
|
|
throw new SSLHandshakeException(errStr + " (error code: " +
|
|
err + ", TID " + Thread.currentThread().getId() + ")");
|
|
}
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"thread exiting ioLock (handshake)");
|
|
}
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"trying to get handshakeLock (handshakeComplete)");
|
|
|
|
synchronized (handshakeLock) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"thread got handshakeLock (handshakeComplete)");
|
|
/* mark handshake completed */
|
|
handshakeComplete = true;
|
|
}
|
|
|
|
/* notify handshake completed listeners */
|
|
if (ret == WolfSSL.SSL_SUCCESS && hsListeners != null) {
|
|
HandshakeCompletedEvent event = new HandshakeCompletedEvent(
|
|
this, EngineHelper.getSession());
|
|
|
|
for (int i = 0; i < hsListeners.size(); i++) {
|
|
hsListeners.get(i).handshakeCompleted(event);
|
|
}
|
|
}
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"completed SSL/TLS handshake, listeners notified");
|
|
|
|
/* print debug info about connection, if enabled */
|
|
if (EngineHelper.getSession() != null) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"SSL/TLS protocol version: " +
|
|
EngineHelper.getSession().getProtocol());
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"SSL/TLS cipher suite: " +
|
|
EngineHelper.getSession().getCipherSuite());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the SSLSocket to use client or server mode.
|
|
*
|
|
* This must be called before the handshake begins on this Socket.
|
|
*
|
|
* @param mode true for client mode, false for server mode
|
|
*
|
|
* @throws IllegalArgumentException if caller tries to set the mode
|
|
* after handshaking has completed
|
|
*/
|
|
@Override
|
|
public synchronized void setUseClientMode(boolean mode)
|
|
throws IllegalArgumentException {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered setUseClientMode()");
|
|
|
|
EngineHelper.setUseClientMode(mode);
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"socket client mode set to: " + mode);
|
|
}
|
|
|
|
/**
|
|
* Return the client mode of this SSLSocket.
|
|
*
|
|
* @return true if in client mode, otherwise false for server mode
|
|
*/
|
|
@Override
|
|
public synchronized boolean getUseClientMode() {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered getUseClientMode()");
|
|
|
|
return EngineHelper.getUseClientMode();
|
|
}
|
|
|
|
/**
|
|
* Configures the SSLSocket to require client authentication.
|
|
*
|
|
* Only useful in server mode. Similar to setWantClientAuth(), but
|
|
* if a client does not provide a cert/method for the server to
|
|
* authenticate it, the connection will fail.
|
|
*
|
|
* @param need true sets client auth requirement, otherwise false
|
|
*/
|
|
@Override
|
|
public synchronized void setNeedClientAuth(boolean need) {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered setNeedClientAuth(need: " + String.valueOf(need) + ")");
|
|
|
|
EngineHelper.setNeedClientAuth(need);
|
|
}
|
|
|
|
/**
|
|
* Return if mandatory client authentication is set for this SSLSocket.
|
|
*
|
|
* @return true if Socket has been configured to require client auth,
|
|
* otherwise false
|
|
*/
|
|
@Override
|
|
public synchronized boolean getNeedClientAuth() {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered getNeedClientAuth()");
|
|
|
|
return EngineHelper.getNeedClientAuth();
|
|
}
|
|
|
|
/**
|
|
* Configures the SSLSocket to request client authentication, but not
|
|
* require it.
|
|
*
|
|
* Similar to setNeedClientAuth(), but the handshake does not abort
|
|
* if the client does not send a certificate back.
|
|
*
|
|
* @param want true to enable server to request certificate from client,
|
|
* false if client auth should be disabled
|
|
*/
|
|
@Override
|
|
public synchronized void setWantClientAuth(boolean want) {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered setWantClientAuth(want: " + String.valueOf(want) + ")");
|
|
|
|
EngineHelper.setWantClientAuth(want);
|
|
}
|
|
|
|
/**
|
|
* Returns true if SSLSocket will request client authentication.
|
|
*
|
|
* "want" client auth indicates that a server socket will request
|
|
* that the client sends a certificate to authenticate itself, but
|
|
* the server will not abort the handshake if the client does not
|
|
* send it.
|
|
*
|
|
* @return true if Socket will request client auth, false otherwise
|
|
*/
|
|
@Override
|
|
public synchronized boolean getWantClientAuth() {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered getWantClientAuth()");
|
|
|
|
return EngineHelper.getWantClientAuth();
|
|
}
|
|
|
|
/**
|
|
* Enables this SSLSocket to create new sessions.
|
|
*
|
|
* If this is set to false, and there are not sessions to resume,
|
|
* this Socket will not be allowed to create new sessions.
|
|
*
|
|
* @param flag true to allow session creation, otherwise false
|
|
*/
|
|
@Override
|
|
public synchronized void setEnableSessionCreation(boolean flag) {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered setEnableSessionCreation(flag: " +
|
|
String.valueOf(flag) + ")");
|
|
|
|
EngineHelper.setEnableSessionCreation(flag);
|
|
}
|
|
|
|
/**
|
|
* Returns whether this SSLSocket can create new sessions.
|
|
*
|
|
* @return true if this Socket can create new sessions, otherwise false
|
|
*/
|
|
@Override
|
|
public synchronized boolean getEnableSessionCreation() {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered getEnableSessionCreation()");
|
|
|
|
return EngineHelper.getEnableSessionCreation();
|
|
}
|
|
|
|
/**
|
|
* Enables use of session tickets with this session. Disabled by default.
|
|
*
|
|
* @param useTickets true to enable session tickets, otherwise false
|
|
*/
|
|
public synchronized void setUseSessionTickets(boolean useTickets) {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered setUseSessionTickets(flag: " +
|
|
String.valueOf(useTickets) + ")");
|
|
|
|
EngineHelper.setUseSessionTickets(useTickets);
|
|
}
|
|
|
|
/**
|
|
* Return the InputStream associated with this SSLSocket.
|
|
*
|
|
* @return InputStream for this Socket
|
|
*
|
|
* @throws IOException if InputStream is not able to be returned
|
|
*/
|
|
@Override
|
|
public synchronized InputStream getInputStream() throws IOException {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered getInputStream()");
|
|
|
|
checkAndInitSSLSocket();
|
|
|
|
if (!this.isConnected()) {
|
|
throw new SocketException("Socket is not connected");
|
|
}
|
|
|
|
if (this.isClosed()) {
|
|
throw new IOException("Socket has been closed");
|
|
}
|
|
|
|
if (inStream == null) {
|
|
inStream = new WolfSSLInputStream(ssl, this);
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"created WolfSSLInputStream");
|
|
}
|
|
|
|
return inStream;
|
|
}
|
|
|
|
/**
|
|
* Return the OutputStream associated with this SSLSocket.
|
|
*
|
|
* @return OutputStream for this Socket
|
|
*
|
|
* @throws IOException if OutputStream is not able to be returned
|
|
*/
|
|
@Override
|
|
public synchronized OutputStream getOutputStream() throws IOException {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered getOutputStream()");
|
|
|
|
checkAndInitSSLSocket();
|
|
|
|
if (!this.isConnected()) {
|
|
throw new SocketException("Socket is not connected");
|
|
}
|
|
|
|
if (this.isClosed()) {
|
|
throw new IOException("Socket has been closed");
|
|
}
|
|
|
|
if (outStream == null) {
|
|
outStream = new WolfSSLOutputStream(ssl, this);
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"created WolfSSLOutputStream");
|
|
}
|
|
|
|
return outStream;
|
|
}
|
|
|
|
/**
|
|
* Set the SO_TIMEOUT with specified timeout in milliseconds.
|
|
* Must be called prior to socket operations to have an effect.
|
|
*
|
|
* @param timeout Read timeout in milliseconds, or 0 for infinite
|
|
*
|
|
* @throws SocketException if there is an error setting the timeout value
|
|
*/
|
|
@Override
|
|
public void setSoTimeout(int timeout) throws SocketException {
|
|
/* timeoutLock synchronizes get/set of timeout */
|
|
synchronized (timeoutLock) {
|
|
if (this.socket != null) {
|
|
this.socket.setSoTimeout(timeout);
|
|
} else {
|
|
super.setSoTimeout(timeout);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the SO_TIMEOUT value, in milliseconds.
|
|
*
|
|
* @return Timeout value in milliseconds, or 0 if disabled/infinite
|
|
*
|
|
* @throws SocketException if there is an error getting timeout value
|
|
*/
|
|
@Override
|
|
public int getSoTimeout() throws SocketException {
|
|
/* timeoutLock synchronizes get/set of timeout */
|
|
synchronized (timeoutLock) {
|
|
if (this.socket != null) {
|
|
return this.socket.getSoTimeout();
|
|
} else {
|
|
return super.getSoTimeout();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the SSLParameters for this SSLSocket.
|
|
*
|
|
* @param params SSLParameters to set for this SSLSocket object
|
|
*/
|
|
public synchronized void setSSLParameters(SSLParameters params) {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered setSSLParameters()");
|
|
|
|
if (params != null) {
|
|
WolfSSLParametersHelper.importParams(params, this.params);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the SSLParameters for this SSLSocket.
|
|
*
|
|
* @return SSLParameters for this SSLSocket object.
|
|
*/
|
|
@Override
|
|
public synchronized SSLParameters getSSLParameters() {
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered getSSLParameters()");
|
|
|
|
return WolfSSLParametersHelper.decoupleParams(this.params);
|
|
}
|
|
|
|
/**
|
|
* Has the underlying WOLFSSL / WolfSSLSession been resumed / reused.
|
|
* This calls down to native wolfSSL_session_reused()
|
|
*
|
|
* NON-STANDARD API, not part of JSSE. Must cast SSLSocket back
|
|
* to WolfSSLSocke to use.
|
|
*
|
|
* @return true if session has been resumed, otherwise false
|
|
* @throws SSLException if native JNI call fails or underlying
|
|
* WolfSSLSession has been freed
|
|
*/
|
|
public synchronized boolean sessionResumed() throws SSLException {
|
|
if (this.ssl != null) {
|
|
try {
|
|
int resume = this.ssl.sessionReused();
|
|
if (resume == 1) {
|
|
return true;
|
|
}
|
|
} catch (IllegalStateException | WolfSSLJNIException e) {
|
|
throw new SSLException(e);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Closes this SSLSocket.
|
|
*
|
|
* If this socket was created with an autoClose value set to true,
|
|
* this will also close the underlying Socket.
|
|
*
|
|
* @throws IOException upon error closing the connection
|
|
*/
|
|
@Override
|
|
public synchronized void close() throws IOException {
|
|
|
|
int ret;
|
|
boolean beforeObjectInit = false;
|
|
boolean handshakeFinished = false;
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered close()");
|
|
|
|
/* Test if this is called before WolfSSLSocket object has initialized,
|
|
* meaning this is called directly from super(). If so, we skip
|
|
* TLS-specific shutdown since not relevant. */
|
|
try {
|
|
synchronized (handshakeLock) { }
|
|
} catch (NullPointerException e) {
|
|
beforeObjectInit = true;
|
|
}
|
|
|
|
try {
|
|
if (beforeObjectInit == false) {
|
|
|
|
checkAndInitSSLSocket();
|
|
|
|
/* Check if underlying Socket is still open before closing,
|
|
* in case application calls SSLSocket.close() multiple times */
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"trying to get handshakeLock (close)");
|
|
|
|
synchronized (handshakeLock) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"thread got handshakeLock (close)");
|
|
|
|
if (this.connectionClosed == true ||
|
|
(this.socket != null && this.socket.isClosed()) ||
|
|
(this.socket == null && super.isClosed())) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"Socket already closed, skipping TLS shutdown");
|
|
return;
|
|
}
|
|
|
|
/* Get value of handshakeComplete while inside lock */
|
|
handshakeFinished = this.handshakeComplete;
|
|
}
|
|
|
|
/* Try TLS shutdown procedure, only if handshake has finished */
|
|
if (ssl != null && handshakeFinished) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"shutting down SSL/TLS connection");
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"thread trying to get ioLock (shutdown)");
|
|
|
|
synchronized (ioLock) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"thread got ioLock (shutdown)");
|
|
|
|
synchronized (handshakeLock) {
|
|
if (this.getUseClientMode() == true &&
|
|
this.handshakeComplete == true) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"saving WOLFSSL_SESSION into cache");
|
|
EngineHelper.saveSession();
|
|
}
|
|
else {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"not saving WOLFSSL_SESSION into cache, " +
|
|
"not client or handshake not complete");
|
|
}
|
|
}
|
|
|
|
try {
|
|
if (this.socket != null) {
|
|
ret = ssl.shutdownSSL(
|
|
this.socket.getSoTimeout());
|
|
} else {
|
|
ret = ssl.shutdownSSL(
|
|
super.getSoTimeout());
|
|
}
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"ssl.shutdownSSL() ret = " + ret);
|
|
|
|
} catch (SocketException | SocketTimeoutException e) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"Exception while trying to ssl.shutdownSSL(), " +
|
|
"ignoring to finish cleanup");
|
|
}
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"thread trying to get handshakeLock");
|
|
|
|
synchronized (handshakeLock) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"thread got handshakeLock");
|
|
|
|
this.connectionClosed = true;
|
|
|
|
/* 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(). */
|
|
Object readCtx = this.ssl.getIOReadCtx();
|
|
if (readCtx != null &&
|
|
readCtx instanceof ConsumedRecvCtx) {
|
|
ConsumedRecvCtx rctx = (ConsumedRecvCtx)readCtx;
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"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;
|
|
|
|
/* Release Input/OutputStream objects. Do not
|
|
* close WolfSSLSocket inside stream close,
|
|
* since we handle that next below and do
|
|
* differently depending on if autoClose has been
|
|
* set or not. */
|
|
if (this.inStream != null) {
|
|
this.inStream.close(false);
|
|
this.inStream = null;
|
|
}
|
|
if (this.outStream != null) {
|
|
this.outStream.close(false);
|
|
this.outStream = null;
|
|
}
|
|
|
|
} /* handshakeLock */
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"thread exiting ioLock (shutdown)");
|
|
|
|
} /* ioLock */
|
|
}
|
|
}
|
|
|
|
if (this.autoClose) {
|
|
if (this.socket != null) {
|
|
this.socket.close();
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"socket (external) closed: " + this.socket);
|
|
} else {
|
|
super.close();
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"socket (super) closed: " + super.toString());
|
|
}
|
|
} else {
|
|
if (this.socket != null) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"socket (external) not closed, autoClose set to false");
|
|
} else {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"socket (super) not closed, autoClose set to false");
|
|
}
|
|
}
|
|
|
|
} catch (IllegalStateException e) {
|
|
throw new IOException(e);
|
|
} catch (WolfSSLJNIException jnie) {
|
|
throw new IOException(jnie);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Bind socket to local address.
|
|
*/
|
|
@Override
|
|
public void bind(SocketAddress bindpoint) throws IOException {
|
|
if (this.socket != null) {
|
|
this.socket.bind(bindpoint);
|
|
} else {
|
|
super.bind(bindpoint);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Connects the underlying Socket associated with this SSLSocket.
|
|
*
|
|
* @param endpoint address of peer to connect underlying Socket to
|
|
*
|
|
* @throws IOException upon error connecting Socket
|
|
*/
|
|
@Override
|
|
public synchronized void connect(SocketAddress endpoint)
|
|
throws IOException {
|
|
|
|
InetSocketAddress address = null;
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered connect(SocketAddress endpoint)");
|
|
|
|
if (!(endpoint instanceof InetSocketAddress)) {
|
|
throw new IllegalArgumentException("endpoint is not of type " +
|
|
"InetSocketAddress");
|
|
}
|
|
|
|
if (this.socket != null) {
|
|
this.socket.connect(endpoint);
|
|
} else {
|
|
super.connect(endpoint);
|
|
}
|
|
|
|
address = (InetSocketAddress)endpoint;
|
|
|
|
/* register host/port for session resumption in case where
|
|
createSocket() was called without host/port, but
|
|
SSLSocket.connect() was explicitly called with SocketAddress */
|
|
if (address != null && EngineHelper != null) {
|
|
EngineHelper.setHostAndPort(
|
|
address.getAddress().getHostAddress(),
|
|
address.getPort());
|
|
EngineHelper.setPeerAddress(address.getAddress());
|
|
}
|
|
|
|
/* if user is calling after WolfSSLSession creation, register
|
|
socket fd with native wolfSSL */
|
|
if (ssl != null) {
|
|
checkAndInitSSLSocket();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Connects the underlying Socket associated with this SSLSocket.
|
|
*
|
|
* @param endpoint address of peer to connect underlying socket to
|
|
* @param timeout timeout value to set for underlying Socket connection
|
|
*
|
|
* @throws IOException upon error connecting Socket
|
|
*/
|
|
@Override
|
|
public synchronized void connect(SocketAddress endpoint, int timeout)
|
|
throws IOException {
|
|
|
|
InetSocketAddress address = null;
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"entered connect(SocketAddress endpoint, int timeout / " +
|
|
timeout + " ms)");
|
|
|
|
if (!(endpoint instanceof InetSocketAddress)) {
|
|
throw new IllegalArgumentException("endpoint is not of type " +
|
|
"InetSocketAddress");
|
|
}
|
|
|
|
if (this.socket != null) {
|
|
this.socket.connect(endpoint, timeout);
|
|
} else {
|
|
super.connect(endpoint, timeout);
|
|
}
|
|
|
|
address = (InetSocketAddress)endpoint;
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"Underlying Java Socket connected to peer: " + address);
|
|
|
|
/* register host/port for session resumption in case where
|
|
createSocket() was called without host/port, but
|
|
SSLSocket.connect() was explicitly called with SocketAddress */
|
|
if (address != null && EngineHelper != null) {
|
|
EngineHelper.setHostAndPort(
|
|
address.getAddress().getHostAddress(),
|
|
address.getPort());
|
|
EngineHelper.setPeerAddress(address.getAddress());
|
|
}
|
|
|
|
/* if user is calling after WolfSSLSession creation, register
|
|
socket fd with native wolfSSL */
|
|
if (ssl != null) {
|
|
checkAndInitSSLSocket();
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("deprecation")
|
|
@Override
|
|
protected synchronized void finalize() throws Throwable {
|
|
if (this.ssl != null) {
|
|
Object readCtx = this.ssl.getIOReadCtx();
|
|
if (readCtx != null &&
|
|
readCtx instanceof ConsumedRecvCtx) {
|
|
ConsumedRecvCtx rctx = (ConsumedRecvCtx)readCtx;
|
|
rctx.closeDataStreams();
|
|
}
|
|
this.ssl.freeSSL();
|
|
this.ssl = null;
|
|
this.EngineHelper = null;
|
|
this.params = null;
|
|
}
|
|
super.finalize();
|
|
}
|
|
|
|
/**
|
|
* wolfSSL send callback context, used with SocketSendCallback to
|
|
* gain access to the underlying Socket object.
|
|
*/
|
|
class SocketSendCtx {
|
|
private Socket sock = null;
|
|
|
|
public SocketSendCtx(Socket s) {
|
|
this.sock = s;
|
|
}
|
|
|
|
public Socket getSocket() {
|
|
return this.sock;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* wolfSSL receive callback context, used with SocketRecvCallback to
|
|
* gain access to the underlying Socket object.
|
|
*/
|
|
class SocketRecvCtx {
|
|
private Socket sock = null;
|
|
|
|
public SocketRecvCtx(Socket s) {
|
|
this.sock = s;
|
|
}
|
|
|
|
public Socket getSocket() {
|
|
return this.sock;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* wolfSSL send callback used when WolfSSLSocket is created
|
|
* based on an existing Java Socket object, where that Socket is not
|
|
* of type java.net.Socket.
|
|
*
|
|
* This is needed in non java.net.Socket cases since not all those
|
|
* subclasses contain an internal file descriptor (fd), or alternatively
|
|
* expect the calling application to do I/O using the InputStream and
|
|
* OutputStream of the Socket */
|
|
class SocketSendCallback implements WolfSSLIOSendCallback {
|
|
|
|
/**
|
|
* I/O send callback method.
|
|
* This method acts as the I/O send callback to be used with wolfSSL.
|
|
*
|
|
* @param ssl the current SSL session object from which the callback
|
|
* was initiated.
|
|
* @param buf buffer containing data to be sent to the peer.
|
|
* @param sz size of data in buffer "<b>buf</b>"
|
|
* @param ctx I/O context to be used.
|
|
* @return the number of bytes sent, or an error.
|
|
*/
|
|
public int sendCallback(WolfSSLSession ssl,
|
|
byte[] buf, int sz, Object ctx) {
|
|
|
|
SocketSendCtx sendCtx = (SocketSendCtx)ctx;
|
|
Socket sock = sendCtx.getSocket();
|
|
OutputStream outStream = null;
|
|
|
|
if (sock == null) {
|
|
return WolfSSL.WOLFSSL_CBIO_ERR_GENERAL;
|
|
}
|
|
|
|
if (!sock.isConnected() || sock.isClosed()) {
|
|
return WolfSSL.WOLFSSL_CBIO_ERR_CONN_CLOSE;
|
|
}
|
|
|
|
try {
|
|
outStream = sock.getOutputStream();
|
|
outStream.write(buf, 0, sz);
|
|
|
|
} catch (IOException e) {
|
|
if (sock.isClosed()) {
|
|
return WolfSSL.WOLFSSL_CBIO_ERR_CONN_CLOSE;
|
|
}
|
|
return WolfSSL.WOLFSSL_CBIO_ERR_GENERAL;
|
|
|
|
} catch (NullPointerException |
|
|
IndexOutOfBoundsException e) {
|
|
return WolfSSL.WOLFSSL_CBIO_ERR_GENERAL;
|
|
}
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"sent " + sz + " bytes");
|
|
return sz;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* wolfSSL receive callback used when WolfSSLSocket is created
|
|
* based on an existing Java Socket object, where that Socket is not
|
|
* of type java.net.Socket.
|
|
*
|
|
* This is needed in non java.net.Socket cases since not all those
|
|
* subclasses contain an internal file descriptor (fd), or alternatively
|
|
* expect the calling application to do I/O using the InputStream and
|
|
* OutputStream of the Socket */
|
|
class SocketRecvCallback implements WolfSSLIORecvCallback {
|
|
|
|
/**
|
|
* I/O receive callback method.
|
|
* This method acts as the I/O receive callback to be used with wolfSSL.
|
|
*
|
|
* @param ssl the current SSL session object from which the callback
|
|
* was initiated.
|
|
* @param buf buffer in which the application should place data which
|
|
* has been received from the peer.
|
|
* @param sz size of buffer, <b>buf</b>
|
|
* @param ctx I/O context to be used.
|
|
* @return the number of bytes read, or an error.
|
|
*/
|
|
public int receiveCallback(WolfSSLSession ssl,
|
|
byte[] buf, int sz, Object ctx) {
|
|
|
|
int ret = 0;
|
|
SocketRecvCtx recvCtx = (SocketRecvCtx)ctx;
|
|
Socket sock = recvCtx.getSocket();
|
|
InputStream inStream = null;
|
|
|
|
if (sock == null) {
|
|
return WolfSSL.WOLFSSL_CBIO_ERR_GENERAL;
|
|
}
|
|
|
|
if (!sock.isConnected() || sock.isClosed()) {
|
|
return WolfSSL.WOLFSSL_CBIO_ERR_CONN_CLOSE;
|
|
}
|
|
|
|
try {
|
|
/* Try reading from stream, returns -1 on end of stream.
|
|
* Blocks until data is available, end of stream is reached,
|
|
* or an exception is thrown. */
|
|
inStream = sock.getInputStream();
|
|
ret = inStream.read(buf, 0, sz);
|
|
if (ret == -1) {
|
|
return WolfSSL.WOLFSSL_CBIO_ERR_CONN_CLOSE;
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
if (sock.isClosed()) {
|
|
return WolfSSL.WOLFSSL_CBIO_ERR_CONN_CLOSE;
|
|
}
|
|
return WolfSSL.WOLFSSL_CBIO_ERR_GENERAL;
|
|
|
|
} catch (NullPointerException |
|
|
IndexOutOfBoundsException e) {
|
|
return WolfSSL.WOLFSSL_CBIO_ERR_GENERAL;
|
|
}
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"received " + ret + " bytes");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* wolfSSL receive callback context, used with ConsumedRecvCallback to
|
|
* gain access to underlying Socket and InputStream objects.
|
|
*/
|
|
class ConsumedRecvCtx {
|
|
private Socket s = null;
|
|
private DataInputStream consumed = null;
|
|
private DataInputStream sockStream = null;
|
|
|
|
public ConsumedRecvCtx(Socket s, InputStream in) {
|
|
this.s = s;
|
|
this.consumed = new DataInputStream(in);
|
|
}
|
|
|
|
public synchronized DataInputStream getSocketDataStream()
|
|
throws IOException {
|
|
|
|
if (this.s != null) {
|
|
if (this.sockStream == null) {
|
|
this.sockStream =
|
|
new DataInputStream(this.s.getInputStream());
|
|
}
|
|
return this.sockStream;
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public synchronized DataInputStream getConsumedDataStream() {
|
|
return this.consumed;
|
|
}
|
|
|
|
public synchronized void closeDataStreams()
|
|
throws IOException {
|
|
if (consumed != null) {
|
|
consumed.close();
|
|
}
|
|
if (sockStream != null) {
|
|
sockStream.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* wolfSSL receive callback used when WolfSSLSocket is created based
|
|
* on an existing Socket and existing InputStream with data to read.
|
|
*
|
|
* This callback will read all data from the pre-existing/populated
|
|
* InputStream first, then start reading from the Socket proper.
|
|
*/
|
|
class ConsumedRecvCallback implements WolfSSLIORecvCallback {
|
|
|
|
public int receiveCallback(WolfSSLSession ssl, byte[] buf,
|
|
int sz, Object ctx) {
|
|
|
|
int ret = 0;
|
|
|
|
try {
|
|
ConsumedRecvCtx context = (ConsumedRecvCtx)ctx;
|
|
DataInputStream current = context.getSocketDataStream();
|
|
DataInputStream consumed = context.getConsumedDataStream();
|
|
|
|
/* try to read from consumed stream first */
|
|
if (consumed != null && consumed.available() > 0) {
|
|
ret = consumed.read(buf, 0, sz);
|
|
/* if we are at EOF, return 0 */
|
|
if (ret == -1) {
|
|
ret = 0;
|
|
}
|
|
|
|
} else if (current != null) {
|
|
/* read directly from Socket, may throw SocketException
|
|
* if underlying socket is non-blocking and returns
|
|
* WANT_READ. */
|
|
try {
|
|
ret = current.read(buf, 0, sz);
|
|
if (ret == -1) {
|
|
/* no data available, end of stream reached */
|
|
return 0;
|
|
}
|
|
} catch (SocketException se) {
|
|
return WolfSSL.WOLFSSL_CBIO_ERR_WANT_READ;
|
|
}
|
|
}
|
|
} catch (IOException e) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.ERROR,
|
|
"error reading from Socket InputStream");
|
|
ret = WolfSSL.WOLFSSL_CBIO_ERR_GENERAL;
|
|
}
|
|
|
|
/* return size read, or error */
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
class WolfSSLInputStream extends InputStream {
|
|
|
|
private WolfSSLSession ssl;
|
|
private WolfSSLSocket socket;
|
|
private boolean isClosed = true;
|
|
|
|
/* Atomic boolean to indicate if this InputStream has started to
|
|
* close. Protects against deadlock between two threads calling
|
|
* SSLSocket.close() and InputStream.close() simulatenously. */
|
|
private AtomicBoolean isClosing = new AtomicBoolean(false);
|
|
|
|
public WolfSSLInputStream(WolfSSLSession ssl, WolfSSLSocket socket) {
|
|
this.ssl = ssl;
|
|
this.socket = socket; /* parent socket */
|
|
this.isClosed = false;
|
|
}
|
|
|
|
/**
|
|
* Close InputStream, but gives caller option to close underlying
|
|
* Socket or not.
|
|
*
|
|
* @param closeSocket close underlying WolfSSLSocket if set to true,
|
|
* otherwise if false leave WolfSSLSocket open.
|
|
*/
|
|
protected void close(boolean closeSocket) throws IOException {
|
|
|
|
if (isClosing.compareAndSet(false, true)) {
|
|
|
|
synchronized (this) {
|
|
if (closeSocket) {
|
|
if (this.socket == null || this.isClosed) {
|
|
return;
|
|
}
|
|
|
|
if (this.socket.isClosed()) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"socket (input) already closed");
|
|
}
|
|
else {
|
|
this.socket.close();
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"socket (input) closed: " + this.socket);
|
|
}
|
|
}
|
|
|
|
this.socket = null;
|
|
this.ssl = null;
|
|
this.isClosed = true;
|
|
}
|
|
}
|
|
else {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"InputStream already in process of being closed");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Close InputStream, also closes internal WolfSSLSocket.
|
|
*/
|
|
public void close() throws IOException {
|
|
close(true);
|
|
}
|
|
|
|
@Override
|
|
public synchronized int read() throws IOException {
|
|
|
|
int ret;
|
|
byte[] data = new byte[1];
|
|
|
|
try {
|
|
ret = this.read(data, 0, 1);
|
|
|
|
/* check for end of stream and other errors */
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
} catch (NullPointerException ne) {
|
|
throw new IOException(ne);
|
|
|
|
} catch (IndexOutOfBoundsException ioe) {
|
|
throw new IndexOutOfBoundsException(ioe.toString());
|
|
}
|
|
|
|
return (data[0] & 0xFF);
|
|
}
|
|
|
|
public synchronized int read(byte[] b)
|
|
throws NullPointerException, IOException {
|
|
|
|
return this.read(b, 0, b.length);
|
|
}
|
|
|
|
public synchronized int read(byte[] b, int off, int len)
|
|
throws NullPointerException, IndexOutOfBoundsException,
|
|
IOException {
|
|
|
|
int ret = 0;
|
|
|
|
if (b == null) {
|
|
throw new NullPointerException("Input array is null");
|
|
}
|
|
|
|
/* check if socket is closed */
|
|
if (this.isClosed || socket == null || socket.isClosed()) {
|
|
throw new SocketException("Socket is closed");
|
|
}
|
|
|
|
/* check if connection has already been closed/shutdown */
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"trying to get socket.handshakeLock (read)");
|
|
|
|
synchronized (socket.handshakeLock) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"thread got socket.handshakeLock (read)");
|
|
|
|
if (socket.connectionClosed == true) {
|
|
throw new SocketException("Connection already shutdown");
|
|
}
|
|
}
|
|
|
|
/* do handshake if not completed yet, handles synchronization */
|
|
try {
|
|
/* do handshake if not completed yet, handles synchronization */
|
|
if (socket.handshakeComplete == false &&
|
|
socket.handshakeStarted == false) {
|
|
socket.startHandshake();
|
|
}
|
|
} catch (SocketTimeoutException e) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"got socket timeout in read()");
|
|
throw e;
|
|
}
|
|
|
|
if (b.length == 0 || len == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (off < 0 || len < 0 || len > (b.length - off)) {
|
|
throw new IndexOutOfBoundsException(
|
|
"Array index out of bounds");
|
|
}
|
|
|
|
try {
|
|
int err;
|
|
int timeout = socket.getSoTimeout();
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"ssl.read() socket timeout = " + timeout);
|
|
|
|
ret = ssl.read(b, off, len, timeout);
|
|
err = ssl.getError(ret);
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"ssl.read(off: " + off + ", len: " + len + ") ret = " +
|
|
ret + ", err = " + err);
|
|
|
|
/* check for end of stream */
|
|
if ((err == WolfSSL.SSL_ERROR_ZERO_RETURN) ||
|
|
((err == WolfSSL.SSL_ERROR_SOCKET_PEER_CLOSED) &&
|
|
(ret == 0))) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"ssl.read() got SSL_ERROR_ZERO_RETURN, " + err +
|
|
", end of stream");
|
|
|
|
/* End of stream */
|
|
return -1;
|
|
}
|
|
|
|
if (ret < 0) {
|
|
/* other errors besides end of stream or WANT_READ
|
|
* are treated as I/O errors and throw an exception */
|
|
String errStr = WolfSSL.getErrorString(err);
|
|
if (err == WolfSSL.SOCKET_ERROR_E) {
|
|
/* Socket error, indicate to caller by returning
|
|
* end of stream */
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"Native wolfSSL_read() error: " + errStr +
|
|
" (error code: " + err + "ret: " + ret +
|
|
"), end of stream");
|
|
return -1;
|
|
|
|
} else {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"Native wolfSSL_read() error: " + errStr +
|
|
" (error code: " + err + ", ret: " + ret + ")");
|
|
throw new IOException("Native wolfSSL_read() " +
|
|
"error: " + errStr +
|
|
" (error code: " + err + ", ret: " + ret + ")");
|
|
}
|
|
}
|
|
|
|
} catch (SocketException e) {
|
|
/* ssl.read() can throw SocketException from poll() if fd
|
|
* closed or peer shut down connection */
|
|
if (e.getMessage().contains("fd closed during poll") ||
|
|
e.getMessage().contains("disconnected during poll")) {
|
|
/* end of stream */
|
|
return -1;
|
|
}
|
|
throw e;
|
|
|
|
} catch (IllegalStateException e) {
|
|
throw new IOException(e);
|
|
}
|
|
|
|
/* return number of bytes read */
|
|
return ret;
|
|
}
|
|
} /* end WolfSSLInputStream inner class */
|
|
|
|
class WolfSSLOutputStream extends OutputStream {
|
|
|
|
private WolfSSLSession ssl;
|
|
private WolfSSLSocket socket;
|
|
private boolean isClosed = true;
|
|
|
|
/* Atomic boolean to indicate if this InputStream has started to
|
|
* close. Protects against deadlock between two threads calling
|
|
* SSLSocket.close() and InputStream.close() simulatenously. */
|
|
private AtomicBoolean isClosing = new AtomicBoolean(false);
|
|
|
|
public WolfSSLOutputStream(WolfSSLSession ssl, WolfSSLSocket socket) {
|
|
this.ssl = ssl;
|
|
this.socket = socket; /* parent socket */
|
|
this.isClosed = false;
|
|
}
|
|
|
|
/**
|
|
* Close OutputStream, but gives caller option to close underlying
|
|
* Socket or not.
|
|
*
|
|
* @param closeSocket close underlying WolfSSLSocket if set to true,
|
|
* otherwise if false leave WolfSSLSocket open.
|
|
*/
|
|
protected void close(boolean closeSocket) throws IOException {
|
|
|
|
if (isClosing.compareAndSet(false, true)) {
|
|
|
|
synchronized (this) {
|
|
if (closeSocket) {
|
|
if (this.socket == null || this.isClosed) {
|
|
return;
|
|
}
|
|
|
|
if (this.socket.isClosed()) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"socket (output) already closed");
|
|
}
|
|
else {
|
|
this.socket.close();
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"socket (output) closed: " + this.socket);
|
|
}
|
|
}
|
|
|
|
this.socket = null;
|
|
this.ssl = null;
|
|
this.isClosed = true;
|
|
}
|
|
}
|
|
else {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"OutputStream already in process of being closed");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Close OutputStream, also closes internal WolfSSLSocket.
|
|
*/
|
|
public void close() throws IOException {
|
|
this.close(true);
|
|
}
|
|
|
|
public synchronized void write(int b) throws IOException {
|
|
byte[] data = new byte[1];
|
|
data[0] = (byte)(b & 0xFF);
|
|
|
|
this.write(data, 0, 1);
|
|
}
|
|
|
|
public synchronized void write(byte[] b) throws IOException {
|
|
this.write(b, 0, b.length);
|
|
}
|
|
|
|
public synchronized void write(byte[] b, int off, int len)
|
|
throws IOException {
|
|
|
|
int ret;
|
|
|
|
if (b == null) {
|
|
throw new NullPointerException("Input array is null");
|
|
}
|
|
|
|
/* check if socket is closed */
|
|
if (this.isClosed || socket == null || socket.isClosed()) {
|
|
throw new SocketException("Socket is closed");
|
|
}
|
|
|
|
/* check if connection has already been closed/shutdown */
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"trying to get socket.handshakeLock (write)");
|
|
|
|
synchronized (socket.handshakeLock) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"thread got socket.handshakeLock (write)");
|
|
if (socket.connectionClosed == true) {
|
|
throw new SocketException(
|
|
"Connection already shutdown");
|
|
}
|
|
}
|
|
|
|
try {
|
|
/* do handshake if not completed yet, handles synchronization */
|
|
if (socket.handshakeComplete == false &&
|
|
socket.handshakeStarted == false) {
|
|
socket.startHandshake();
|
|
}
|
|
} catch (SocketTimeoutException e) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"got socket timeout in write()");
|
|
throw e;
|
|
}
|
|
|
|
if (off < 0 || len < 0 || (off + len) > b.length) {
|
|
throw new IndexOutOfBoundsException(
|
|
"Array index out of bounds");
|
|
}
|
|
|
|
try {
|
|
int err;
|
|
int timeout = socket.getSoTimeout();
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"ssl.write() socket timeout = " + timeout);
|
|
|
|
ret = ssl.write(b, off, len, timeout);
|
|
err = ssl.getError(ret);
|
|
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"ssl.write(off: " + off + ", len: " + len +
|
|
") returned ret = " + ret + ", err = " + err);
|
|
|
|
/* check for end of stream */
|
|
if (err == WolfSSL.SSL_ERROR_ZERO_RETURN) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"ssl.write() got SSL_ERROR_ZERO_RETURN, " +
|
|
"end of stream");
|
|
|
|
/* check to see if we received a close notify alert.
|
|
* if so, throw SocketException since peer has closed
|
|
* the connection */
|
|
if (ssl.gotCloseNotify() == true) {
|
|
throw new SocketException("Peer closed connection");
|
|
}
|
|
}
|
|
|
|
if (ret < 0) {
|
|
/* print error description string */
|
|
String errStr = WolfSSL.getErrorString(err);
|
|
throw new IOException("Native wolfSSL_write() error: "
|
|
+ errStr + " (error code: " + err + ")");
|
|
}
|
|
|
|
} catch (IllegalStateException e) {
|
|
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
|
"got IllegalStateException: " + e +
|
|
", throwing IOException");
|
|
throw new IOException(e);
|
|
}
|
|
}
|
|
} /* end WolfSSLOutputStream inner class */
|
|
}
|
|
|