blob: 3da0fe485f7ad1c898a819bc8f77f6f5831a8df9 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.mina.filter.ssl;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLEngineResult.Status;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import org.apache.mina.core.RuntimeIoException;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.filterchain.IoFilter.NextFilter;
import org.apache.mina.core.filterchain.IoFilterEvent;
import org.apache.mina.core.future.DefaultWriteFuture;
import org.apache.mina.core.future.WriteFuture;
import org.apache.mina.core.session.IoEventType;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.core.write.DefaultWriteRequest;
import org.apache.mina.core.write.WriteRequest;
import org.apache.mina.filter.ssl.SslFilter.EncryptedWriteRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A helper class using the SSLEngine API to decrypt/encrypt data.
* <p/>
* Each connection has a SSLEngine that is used through the lifetime of the connection.
* We allocate buffers for use as the outbound and inbound network buffers.
* These buffers handle all of the intermediary data for the SSL connection. To make things easy,
* we'll require outNetBuffer be completely flushed before trying to wrap any more data.
* <p/>
* This class is not to be used by any client, it's closely associated with the SSL Filter.
* None of its methods are public as they should not be used by any other class but from
* the SslFilter class, in the same package
*
* @author <a href="http://mina.apache.org">Apache MINA Project</a>
*/
/** No qualifier*/
class SslHandler {
/** A logger for this class */
private static final Logger LOGGER = LoggerFactory.getLogger(SslHandler.class);
/** The SSL Filter which has created this handler */
private final SslFilter sslFilter;
/** The current session */
private final IoSession session;
private final Queue<IoFilterEvent> preHandshakeEventQueue = new ConcurrentLinkedQueue<>();
private final Queue<IoFilterEvent> filterWriteEventQueue = new ConcurrentLinkedQueue<>();
/** A queue used to stack all the incoming data until the SSL session is established */
private final Queue<IoFilterEvent> messageReceivedEventQueue = new ConcurrentLinkedQueue<>();
private SSLEngine sslEngine;
/**
* Encrypted data from the net
*/
private IoBuffer inNetBuffer;
/**
* Encrypted data to be written to the net
*/
private IoBuffer outNetBuffer;
/**
* Application cleartext data to be read by application
*/
private IoBuffer appBuffer;
/**
* Empty buffer used during initial handshake and close operations
*/
private final IoBuffer emptyBuffer = IoBuffer.allocate(0);
private SSLEngineResult.HandshakeStatus handshakeStatus;
/**
* A flag set to true when the first SSL handshake has been completed
* This is used to avoid sending a notification to the application handler
* when we switch to a SECURE or UNSECURE session.
*/
private boolean firstSSLNegociation;
/** A flag set to true when a SSL Handshake has been completed */
private boolean handshakeComplete;
/** A flag used to indicate to the SslFilter that the buffer
* it will write is already encrypted (this will be the case
* for data being produced during the handshake). */
private boolean writingEncryptedData;
/** A lock to protect the SSL flush of events */
private ReentrantLock sslLock = new ReentrantLock();
/** A counter of schedules events */
private final AtomicInteger scheduledEvents = new AtomicInteger(0);
/**
* Create a new SSL Handler, and initialize it.
*
* @param sslContext
* @throws SSLException
*/
/* no qualifier */SslHandler(SslFilter sslFilter, IoSession session) {
this.sslFilter = sslFilter;
this.session = session;
}
/**
* Initialize the SSL handshake.
*
* @throws SSLException If the underlying SSLEngine handshake initialization failed
*/
/* no qualifier */void init() throws SSLException {
if (sslEngine != null) {
// We already have a SSL engine created, no need to create a new one
return;
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("{} Initializing the SSL Handler", sslFilter.getSessionInfo(session));
}
InetSocketAddress peer = (InetSocketAddress) session.getAttribute(SslFilter.PEER_ADDRESS);
// Create the SSL engine here
if (peer == null) {
sslEngine = sslFilter.sslContext.createSSLEngine();
} else {
sslEngine = sslFilter.sslContext.createSSLEngine(peer.getHostName(), peer.getPort());
}
// Initialize the engine in client mode if necessary
sslEngine.setUseClientMode(sslFilter.isUseClientMode());
// Initialize the different SslEngine modes
if (!sslEngine.getUseClientMode()) {
// Those parameters are only valid when in server mode
if (sslFilter.isWantClientAuth()) {
sslEngine.setWantClientAuth(true);
}
if (sslFilter.isNeedClientAuth()) {
sslEngine.setNeedClientAuth(true);
}
}
// Set the cipher suite to use by this SslEngine instance
if (sslFilter.getEnabledCipherSuites() != null) {
sslEngine.setEnabledCipherSuites(sslFilter.getEnabledCipherSuites());
}
// Set the list of enabled protocols
if (sslFilter.getEnabledProtocols() != null) {
sslEngine.setEnabledProtocols(sslFilter.getEnabledProtocols());
}
// TODO : we may not need to call this method...
// However, if we don't call it here, the tests are failing. Why?
handshakeStatus = sslEngine.getHandshakeStatus();
// Default value
writingEncryptedData = false;
// We haven't yet started a SSL negotiation
// set the flags accordingly
firstSSLNegociation = true;
handshakeComplete = false;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("{} SSL Handler Initialization done.", sslFilter.getSessionInfo(session));
}
}
/**
* Release allocated buffers.
*/
/* no qualifier */void destroy() {
if (sslEngine == null) {
return;
}
// Close inbound and flush all remaining data if available.
try {
sslEngine.closeInbound();
} catch (SSLException e) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Unexpected exception from SSLEngine.closeInbound().", e);
}
}
if (outNetBuffer != null) {
outNetBuffer.capacity(sslEngine.getSession().getPacketBufferSize());
} else {
createOutNetBuffer(0);
}
try {
do {
outNetBuffer.clear();
} while (sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf()).bytesProduced() > 0);
} catch (SSLException e) {
// Ignore.
} finally {
outNetBuffer.free();
outNetBuffer = null;
}
sslEngine.closeOutbound();
sslEngine = null;
preHandshakeEventQueue.clear();
}
/**
* @return The SSL filter which has created this handler
*/
/* no qualifier */SslFilter getSslFilter() {
return sslFilter;
}
/* no qualifier */IoSession getSession() {
return session;
}
/**
* Check if we are writing encrypted data.
*/
/* no qualifier */boolean isWritingEncryptedData() {
return writingEncryptedData;
}
/**
* Check if handshake is completed.
*/
/* no qualifier */boolean isHandshakeComplete() {
return handshakeComplete;
}
/**
* Check if handshake is on going.
*/
/* no qualifier */boolean notHandshaking() {
return handshakeStatus == HandshakeStatus.FINISHED || handshakeStatus == HandshakeStatus.NOT_HANDSHAKING;
}
/* no qualifier */boolean isInboundDone() {
return sslEngine == null || sslEngine.isInboundDone();
}
/* no qualifier */boolean isOutboundDone() {
return sslEngine == null || sslEngine.isOutboundDone();
}
/**
* Check if there is any need to complete handshake.
*/
/* no qualifier */boolean needToCompleteHandshake() {
return handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !isInboundDone();
}
/* no qualifier */void schedulePreHandshakeWriteRequest(NextFilter nextFilter, WriteRequest writeRequest) {
preHandshakeEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
}
/* no qualifier */void flushPreHandshakeEvents() throws SSLException {
IoFilterEvent scheduledWrite;
while ((scheduledWrite = preHandshakeEventQueue.poll()) != null) {
sslFilter
.filterWrite(scheduledWrite.getNextFilter(), session, (WriteRequest) scheduledWrite.getParameter());
}
}
/* no qualifier */void scheduleFilterWrite(NextFilter nextFilter, WriteRequest writeRequest) {
filterWriteEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
}
/**
* Push the newly received data into a queue, waiting for the SSL session
* to be fully established
*
* @param nextFilter The next filter to call
* @param message The incoming data
*/
/* no qualifier */void scheduleMessageReceived(NextFilter nextFilter, Object message) {
messageReceivedEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.MESSAGE_RECEIVED, session, message));
}
/* no qualifier */void flushScheduledEvents() {
scheduledEvents.incrementAndGet();
// Fire events only when the lock is available for this handler.
if (sslLock.tryLock()) {
IoFilterEvent event;
try {
do {
// We need synchronization here inevitably because filterWrite can be
// called simultaneously and cause 'bad record MAC' integrity error.
while ((event = filterWriteEventQueue.poll()) != null) {
NextFilter nextFilter = event.getNextFilter();
nextFilter.filterWrite(session, (WriteRequest) event.getParameter());
}
while ((event = messageReceivedEventQueue.poll()) != null) {
NextFilter nextFilter = event.getNextFilter();
nextFilter.messageReceived(session, event.getParameter());
}
} while (scheduledEvents.decrementAndGet() > 0);
} finally {
sslLock.unlock();
}
}
}
/**
* Call when data are read from net. It will perform the initial hanshake or decrypt
* the data if SSL has been initialiaed.
*
* @param buf buffer to decrypt
* @param nextFilter Next filter in chain
* @throws SSLException on errors
*/
/* no qualifier */void messageReceived(NextFilter nextFilter, ByteBuffer buf) throws SSLException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("{} Processing the received message", sslFilter.getSessionInfo(session));
}
// append buf to inNetBuffer
if (inNetBuffer == null) {
inNetBuffer = IoBuffer.allocate(buf.remaining()).setAutoExpand(true);
}
inNetBuffer.put(buf);
if (!handshakeComplete) {
handshake(nextFilter);
} else {
// Prepare the net data for reading.
inNetBuffer.flip();
if (!inNetBuffer.hasRemaining()) {
return;
}
SSLEngineResult res = unwrap();
// prepare to be written again
if (inNetBuffer.hasRemaining()) {
inNetBuffer.compact();
} else {
inNetBuffer.free();
inNetBuffer = null;
}
checkStatus(res);
renegotiateIfNeeded(nextFilter, res);
}
if (isInboundDone()) {
// Rewind the MINA buffer if not all data is processed and inbound
// is finished.
int inNetBufferPosition = inNetBuffer == null ? 0 : inNetBuffer.position();
buf.position(buf.position() - inNetBufferPosition);
if (inNetBuffer != null) {
inNetBuffer.free();
inNetBuffer = null;
}
}
}
/**
* Get decrypted application data.
*
* @return buffer with data
*/
/* no qualifier */IoBuffer fetchAppBuffer() {
if (appBuffer == null) {
return IoBuffer.allocate(0);
} else {
IoBuffer newAppBuffer = appBuffer.flip();
appBuffer = null;
return newAppBuffer.shrink();
}
}
/**
* Get encrypted data to be sent.
*
* @return buffer with data
*/
/* no qualifier */IoBuffer fetchOutNetBuffer() {
IoBuffer answer = outNetBuffer;
if (answer == null) {
return emptyBuffer;
}
outNetBuffer = null;
return answer.shrink();
}
/**
* Encrypt provided buffer. Encrypted data returned by getOutNetBuffer().
*
* @param src
* data to encrypt
* @throws SSLException
* on errors
*/
/* no qualifier */void encrypt(ByteBuffer src) throws SSLException {
if (!handshakeComplete) {
throw new IllegalStateException();
}
if (!src.hasRemaining()) {
if (outNetBuffer == null) {
outNetBuffer = emptyBuffer;
}
return;
}
createOutNetBuffer(src.remaining());
// Loop until there is no more data in src
while (src.hasRemaining()) {
SSLEngineResult result = sslEngine.wrap(src, outNetBuffer.buf());
if (result.getStatus() == SSLEngineResult.Status.OK) {
if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
doTasks();
}
} else if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
outNetBuffer.capacity(outNetBuffer.capacity() << 1);
outNetBuffer.limit(outNetBuffer.capacity());
} else {
throw new SSLException("SSLEngine error during encrypt: " + result.getStatus() + " src: " + src
+ "outNetBuffer: " + outNetBuffer);
}
}
outNetBuffer.flip();
}
/**
* Start SSL shutdown process.
*
* @return <tt>true</tt> if shutdown process is started. <tt>false</tt> if
* shutdown process is already finished.
* @throws SSLException
* on errors
*/
/* no qualifier */boolean closeOutbound() throws SSLException {
if (sslEngine == null || sslEngine.isOutboundDone()) {
return false;
}
sslEngine.closeOutbound();
createOutNetBuffer(0);
SSLEngineResult result;
for (;;) {
result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
outNetBuffer.capacity(outNetBuffer.capacity() << 1);
outNetBuffer.limit(outNetBuffer.capacity());
} else {
break;
}
}
if (result.getStatus() != SSLEngineResult.Status.CLOSED) {
throw new SSLException("Improper close state: " + result);
}
outNetBuffer.flip();
return true;
}
/**
* @param res
* @throws SSLException
*/
private void checkStatus(SSLEngineResult res) throws SSLException {
SSLEngineResult.Status status = res.getStatus();
/*
* The status may be:
* OK - Normal operation
* OVERFLOW - Should never happen since the application buffer is sized to hold the maximum
* packet size.
* UNDERFLOW - Need to read more data from the socket. It's normal.
* CLOSED - The other peer closed the socket. Also normal.
*/
switch (status) {
case BUFFER_OVERFLOW:
throw new SSLException("SSLEngine error during decrypt: " + status + " inNetBuffer: " + inNetBuffer
+ "appBuffer: " + appBuffer);
case CLOSED:
Exception exception =new RuntimeIoException("SSL/TLS close_notify received");
// Empty the Ssl queue
for (IoFilterEvent event:filterWriteEventQueue) {
EncryptedWriteRequest writeRequest = (EncryptedWriteRequest)event.getParameter();
WriteFuture writeFuture = writeRequest.getParentRequest().getFuture();
writeFuture.setException(exception);
writeFuture.notifyAll();
}
// Empty the session queue
while (!session.getWriteRequestQueue().isEmpty(session)) {
WriteRequest writeRequest = session.getWriteRequestQueue().poll( session );
WriteFuture writeFuture = writeRequest.getFuture();
writeFuture.setException(exception);
writeFuture.notifyAll();
}
// We *must* shutdown session
session.closeNow();
break;
default:
break;
}
}
/**
* Perform any handshaking processing.
*/
/* no qualifier */void handshake(NextFilter nextFilter) throws SSLException {
for (;;) {
switch (handshakeStatus) {
case FINISHED:
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("{} processing the FINISHED state", sslFilter.getSessionInfo(session));
}
session.setAttribute(SslFilter.SSL_SESSION, sslEngine.getSession());
handshakeComplete = true;
// Send the SECURE message only if it's the first SSL handshake
if (firstSSLNegociation) {
firstSSLNegociation = false;
nextFilter.event(session, SslEvent.SECURED);
}
if (LOGGER.isDebugEnabled()) {
if (!isOutboundDone()) {
LOGGER.debug("{} is now secured", sslFilter.getSessionInfo(session));
} else {
LOGGER.debug("{} is not secured yet", sslFilter.getSessionInfo(session));
}
}
return;
case NEED_TASK:
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("{} processing the NEED_TASK state", sslFilter.getSessionInfo(session));
}
handshakeStatus = doTasks();
break;
case NEED_UNWRAP:
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("{} processing the NEED_UNWRAP state", sslFilter.getSessionInfo(session));
}
// we need more data read
SSLEngineResult.Status status = unwrapHandshake(nextFilter);
if (status == SSLEngineResult.Status.BUFFER_UNDERFLOW
&& handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED || isInboundDone()) {
// We need more data or the session is closed
return;
}
break;
case NEED_WRAP:
case NOT_HANDSHAKING:
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("{} processing the NEED_WRAP state", sslFilter.getSessionInfo(session));
}
// First make sure that the out buffer is completely empty.
// Since we cannot call wrap with data left on the buffer
if (outNetBuffer != null && outNetBuffer.hasRemaining()) {
return;
}
SSLEngineResult result;
createOutNetBuffer(0);
result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
while ( result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW ) {
outNetBuffer.capacity(outNetBuffer.capacity() << 1);
outNetBuffer.limit(outNetBuffer.capacity());
result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
}
outNetBuffer.flip();
handshakeStatus = result.getHandshakeStatus();
writeNetBuffer(nextFilter);
break;
default:
String msg = "Invalid Handshaking State" + handshakeStatus
+ " while processing the Handshake for session " + session.getId();
LOGGER.error(msg);
throw new IllegalStateException(msg);
}
}
}
private void createOutNetBuffer(int expectedRemaining) {
// SSLEngine requires us to allocate unnecessarily big buffer
// even for small data. *Shrug*
int capacity = Math.max(expectedRemaining, sslEngine.getSession().getPacketBufferSize());
if (outNetBuffer != null) {
outNetBuffer.capacity(capacity);
} else {
outNetBuffer = IoBuffer.allocate(capacity).minimumCapacity(0);
}
}
/* no qualifier */WriteFuture writeNetBuffer(NextFilter nextFilter) throws SSLException {
// Check if any net data needed to be writen
if (outNetBuffer == null || !outNetBuffer.hasRemaining()) {
// no; bail out
return null;
}
// set flag that we are writing encrypted data
// (used in SSLFilter.filterWrite())
writingEncryptedData = true;
// write net data
WriteFuture writeFuture = null;
try {
IoBuffer writeBuffer = fetchOutNetBuffer();
writeFuture = new DefaultWriteFuture(session);
sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(writeBuffer, writeFuture));
// loop while more writes required to complete handshake
while (needToCompleteHandshake()) {
try {
handshake(nextFilter);
} catch (SSLException ssle) {
SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
newSsle.initCause(ssle);
throw newSsle;
}
IoBuffer currentOutNetBuffer = fetchOutNetBuffer();
if (currentOutNetBuffer != null && currentOutNetBuffer.hasRemaining()) {
writeFuture = new DefaultWriteFuture(session);
sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(currentOutNetBuffer, writeFuture));
}
}
} finally {
writingEncryptedData = false;
}
return writeFuture;
}
private SSLEngineResult.Status unwrapHandshake(NextFilter nextFilter) throws SSLException {
// Prepare the net data for reading.
if (inNetBuffer != null) {
inNetBuffer.flip();
}
if ((inNetBuffer == null) || !inNetBuffer.hasRemaining()) {
// Need more data.
return SSLEngineResult.Status.BUFFER_UNDERFLOW;
}
SSLEngineResult res = unwrap();
handshakeStatus = res.getHandshakeStatus();
checkStatus(res);
// If handshake finished, no data was produced, and the status is still
// ok, try to unwrap more
if ((handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED)
&& (res.getStatus() == SSLEngineResult.Status.OK)
&& inNetBuffer.hasRemaining()) {
res = unwrap();
// prepare to be written again
if (inNetBuffer.hasRemaining()) {
inNetBuffer.compact();
} else {
inNetBuffer.free();
inNetBuffer = null;
}
renegotiateIfNeeded(nextFilter, res);
} else {
// prepare to be written again
if (inNetBuffer.hasRemaining()) {
inNetBuffer.compact();
} else {
inNetBuffer.free();
inNetBuffer = null;
}
}
return res.getStatus();
}
private void renegotiateIfNeeded(NextFilter nextFilter, SSLEngineResult res) throws SSLException {
if ((res.getStatus() != SSLEngineResult.Status.CLOSED)
&& (res.getStatus() != SSLEngineResult.Status.BUFFER_UNDERFLOW)
&& (res.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)) {
// Renegotiation required.
handshakeComplete = false;
handshakeStatus = res.getHandshakeStatus();
handshake(nextFilter);
}
}
/**
* Decrypt the incoming buffer and move the decrypted data to an
* application buffer.
*/
private SSLEngineResult unwrap() throws SSLException {
// We first have to create the application buffer if it does not exist
if (appBuffer == null) {
appBuffer = IoBuffer.allocate(inNetBuffer.remaining());
} else {
// We already have one, just add the new data into it
appBuffer.expand(inNetBuffer.remaining());
}
SSLEngineResult res;
Status status;
HandshakeStatus localHandshakeStatus;
do {
// Decode the incoming data
res = sslEngine.unwrap(inNetBuffer.buf(), appBuffer.buf());
status = res.getStatus();
// We can be processing the Handshake
localHandshakeStatus = res.getHandshakeStatus();
if (status == SSLEngineResult.Status.BUFFER_OVERFLOW) {
// We have to grow the target buffer, it's too small.
// Then we can call the unwrap method again
int newCapacity = sslEngine.getSession().getApplicationBufferSize();
if (appBuffer.remaining() >= newCapacity) {
// The buffer is already larger than the max buffer size suggested by the SSL engine.
// Raising it any more will not make sense and it will end up in an endless loop. Throwing an error is safer
throw new SSLException("SSL buffer overflow");
}
appBuffer.expand(newCapacity);
continue;
}
} while (((status == SSLEngineResult.Status.OK) || (status == SSLEngineResult.Status.BUFFER_OVERFLOW))
&& ((localHandshakeStatus == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) ||
(localHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)));
return res;
}
/**
* Do all the outstanding handshake tasks in the current Thread.
*/
private SSLEngineResult.HandshakeStatus doTasks() {
/*
* We could run this in a separate thread, but I don't see the need for
* this when used from SSLFilter. Use thread filters in MINA instead?
*/
Runnable runnable;
while ((runnable = sslEngine.getDelegatedTask()) != null) {
// TODO : we may have to use a thread pool here to improve the
// performances
runnable.run();
}
return sslEngine.getHandshakeStatus();
}
/**
* Creates a new MINA buffer that is a deep copy of the remaining bytes in
* the given buffer (between index buf.position() and buf.limit())
*
* @param src
* the buffer to copy
* @return the new buffer, ready to read from
*/
/* no qualifier */static IoBuffer copy(ByteBuffer src) {
IoBuffer copy = IoBuffer.allocate(src.remaining());
copy.put(src);
copy.flip();
return copy;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("SSLStatus <");
if (handshakeComplete) {
sb.append("SSL established");
} else {
sb.append("Processing Handshake").append("; ");
sb.append("Status : ").append(handshakeStatus).append("; ");
}
sb.append(", ");
sb.append("HandshakeComplete :").append(handshakeComplete).append(", ");
sb.append(">");
return sb.toString();
}
/**
* Free the allocated buffers
*/
/* no qualifier */void release() {
if (inNetBuffer != null) {
inNetBuffer.free();
inNetBuffer = null;
}
if (outNetBuffer != null) {
outNetBuffer.free();
outNetBuffer = null;
}
}
}