From 1dfff197eadcf24823d7915e6eead2a850f679f9 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Tue, 14 Feb 2012 16:09:28 +0000
Subject: [PATCH] Fix OPENDJ-420: Rare SSLExceptions while handling LDAPS connections and big LDAP searches

---
 opends/src/server/org/opends/server/extensions/TLSByteChannel.java |  737 ++++++++++++++++++++++++++++++--------------------------
 1 files changed, 393 insertions(+), 344 deletions(-)

diff --git a/opends/src/server/org/opends/server/extensions/TLSByteChannel.java b/opends/src/server/org/opends/server/extensions/TLSByteChannel.java
index ad2b96e..9ddb118 100644
--- a/opends/src/server/org/opends/server/extensions/TLSByteChannel.java
+++ b/opends/src/server/org/opends/server/extensions/TLSByteChannel.java
@@ -42,6 +42,7 @@
 import java.util.Set;
 
 import javax.net.ssl.*;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
 
 import org.opends.server.admin.std.server.LDAPConnectionHandlerCfg;
 import org.opends.server.api.ClientConnection;
@@ -53,50 +54,375 @@
 /**
  * A class that provides a TLS byte channel implementation.
  */
-public class TLSByteChannel implements ByteChannel, ConnectionSecurityProvider
+public final class TLSByteChannel implements ConnectionSecurityProvider
 {
-  private static final DebugTracer TRACER = getTracer();
+  /**
+   * Private implementation.
+   */
+  private final class ByteChannelImpl implements ByteChannel
+  {
 
-  private final ByteChannel socketChannel;
+    /**
+     * {@inheritDoc}
+     */
+    public void close() throws IOException
+    {
+      synchronized (readLock)
+      {
+        synchronized (writeLock)
+        {
+          final boolean isInitiator = !sslEngine.isInboundDone();
 
-  private final SSLEngine sslEngine;
+          try
+          {
+            if (!sslEngine.isOutboundDone())
+            {
+              sslEngine.closeOutbound();
+              while (doWrapAndSend(EMPTY_BUFFER) > 0)
+              {
+                // Write out any remaining SSL close notifications.
+              }
+            }
+          }
+          catch (final ClosedChannelException e)
+          {
+            // Ignore this so that close is idempotent.
+          }
+          finally
+          {
+            try
+            {
+              sslEngine.closeInbound();
+            }
+            catch (final SSLException e)
+            {
+              // Not yet received peer's close notification. Ignore this if we
+              // are the initiator.
+              if (!isInitiator)
+              {
+                throw e;
+              }
+            }
+            finally
+            {
+              channel.close();
+            }
+          }
+        }
+      }
+    }
 
-  // read copy to buffer
-  private final ByteBuffer appData;
 
-  // read encrypted
-  private final ByteBuffer appNetData;
 
-  // Write encrypted
-  private final ByteBuffer netData;
-  private final ByteBuffer tempData;
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isOpen()
+    {
+      return !sslEngine.isOutboundDone() || !sslEngine.isInboundDone();
+    }
 
-  private final int sslBufferSize;
-  private final int appBufSize;
 
-  private boolean reading = false;
+
+    /**
+     * {@inheritDoc}
+     */
+    public int read(final ByteBuffer unwrappedData) throws IOException
+    {
+      synchronized (readLock)
+      {
+        // Repeat until there is some unwrapped data available or all available
+        // data has been read from the underlying socket.
+        if (!recvUnwrappedBuffer.hasRemaining())
+        {
+          final int read = doRecvAndUnwrap();
+          if (read <= 0)
+          {
+            // No data read or end of stream.
+            return read;
+          }
+        }
+
+        // Copy available data.
+        final int startPos = unwrappedData.position();
+        if (recvUnwrappedBuffer.remaining() > unwrappedData.remaining())
+        {
+          // Unwrapped data does not fit in client buffer so copy one by at a
+          // time: it's annoying that there is no easy way to do this with
+          // ByteBuffers.
+          while (unwrappedData.hasRemaining())
+          {
+            unwrappedData.put(recvUnwrappedBuffer.get());
+          }
+        }
+        else
+        {
+          // Unwrapped data fits client buffer so block copy.
+          unwrappedData.put(recvUnwrappedBuffer);
+        }
+        return unwrappedData.position() - startPos;
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public int write(final ByteBuffer unwrappedData) throws IOException
+    {
+      // This method will block until the entire message is sent.
+      final int bytesWritten = unwrappedData.remaining();
+
+      // Synchronized in order to prevent interleaving and reordering.
+      synchronized (writeLock)
+      {
+        // Repeat until the entire input data is written.
+        while (unwrappedData.hasRemaining())
+        {
+          // Wrap and send the data.
+          doWrapAndSend(unwrappedData);
+
+          // Perform handshake if needed.
+          if (isHandshaking(sslEngine.getHandshakeStatus()))
+          {
+            doHandshake(false /* isReading */);
+          }
+        }
+      }
+
+      return bytesWritten;
+    }
+
+
+
+    private void doHandshake(final boolean isReading) throws IOException
+    {
+      // This lock is probably unnecessary since tasks can be run in parallel,
+      // but it adds no additional overhead so there's little harm in having
+      // it.
+      synchronized (handshakeLock)
+      {
+        while (true)
+        {
+          switch (sslEngine.getHandshakeStatus())
+          {
+          case NEED_TASK:
+            Runnable runnable;
+            while ((runnable = sslEngine.getDelegatedTask()) != null)
+            {
+              runnable.run();
+            }
+            break;
+          case NEED_UNWRAP:
+            // Block for writes, but be non-blocking for reads.
+            if (isReading)
+            {
+              // Let doRecvAndUnwrap() deal with this.
+              return;
+            }
+
+            // Need to do an unwrap (read) while writing.
+            if (doRecvAndUnwrap() < 0)
+            {
+              throw new ClosedChannelException();
+            }
+            break;
+          case NEED_WRAP:
+            doWrapAndSend(EMPTY_BUFFER);
+            break;
+          default: // NOT_HANDSHAKING, FINISHED.
+            return;
+          }
+        }
+      }
+    }
+
+
+
+    // Attempt to read and unwrap the next SSL packet.
+    private int doRecvAndUnwrap() throws IOException
+    {
+      // Synchronize SSL unwrap with channel reads.
+      synchronized (unwrapLock)
+      {
+        // Repeat if there is underflow or overflow.
+        boolean needRead = true;
+        while (true)
+        {
+          // Read wrapped data if needed.
+          if (needRead)
+          {
+            recvWrappedBuffer.compact(); // Prepare for append.
+            final int read = channel.read(recvWrappedBuffer);
+            recvWrappedBuffer.flip(); // Restore for read.
+            if (read < 0)
+            {
+              // Peer abort?
+              sslEngine.closeInbound();
+              return -1;
+            }
+          }
+          else
+          {
+            needRead = true;
+          }
+
+          // Unwrap.
+          recvUnwrappedBuffer.compact(); // Prepare for append.
+          final SSLEngineResult result = sslEngine.unwrap(recvWrappedBuffer,
+              recvUnwrappedBuffer);
+          recvUnwrappedBuffer.flip(); // Restore for read.
+
+          switch (result.getStatus())
+          {
+          case BUFFER_OVERFLOW:
+            // The unwrapped buffer is not big enough: resize and repeat.
+            final int newAppSize = sslEngine.getSession()
+                .getApplicationBufferSize();
+            final ByteBuffer newRecvUnwrappedBuffer = ByteBuffer
+                .allocate(recvUnwrappedBuffer.limit() + newAppSize);
+            newRecvUnwrappedBuffer.put(recvUnwrappedBuffer);
+            newRecvUnwrappedBuffer.flip();
+            recvUnwrappedBuffer = newRecvUnwrappedBuffer;
+            needRead = false;
+            break; // Retry unwrap.
+          case BUFFER_UNDERFLOW:
+            // Not enough data was read. This either means that the inbound
+            // buffer was too small, or not enough data is available.
+            final int newPktSize = sslEngine.getSession().getPacketBufferSize();
+            if (newPktSize > recvWrappedBuffer.capacity())
+            {
+              // Buffer needs resizing.
+              final ByteBuffer newRecvWrappedBuffer = ByteBuffer
+                  .allocate(newPktSize);
+              newRecvWrappedBuffer.put(recvWrappedBuffer);
+              newRecvWrappedBuffer.flip();
+              recvWrappedBuffer = newRecvWrappedBuffer;
+              break;
+            }
+            else
+            {
+              // Not enough data is available to read a complete SSL packet.
+              return 0;
+            }
+          case CLOSED:
+            // Peer sent SSL close notification.
+            sslEngine.closeInbound();
+            return -1;
+          default: // OK
+            if (recvUnwrappedBuffer.hasRemaining())
+            {
+              // Some application data was read so return it.
+              return recvUnwrappedBuffer.remaining();
+            }
+            else if (isHandshaking(result.getHandshakeStatus()))
+            {
+              // No application data was read, but if we are handshaking then
+              // try to continue.
+              if (result.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP
+                  && !recvWrappedBuffer.hasRemaining())
+              {
+                // Not enough data is available to continue handshake.
+                return 0;
+              }
+              else
+              {
+                // Continue handshake.
+                doHandshake(true /* isReading */);
+              }
+            }
+            else
+            {
+              // No data available and not handshaking.
+              return 0;
+            }
+          }
+        }
+      }
+    }
+
+
+
+    // Attempt to wrap and send the next SSL packet.
+    private int doWrapAndSend(final ByteBuffer unwrappedData)
+        throws IOException
+    {
+      // Synchronize SSL wrap with channel writes.
+      synchronized (wrapLock)
+      {
+        // Repeat while there is overflow.
+        while (true)
+        {
+          final SSLEngineResult result = sslEngine.wrap(unwrappedData,
+              sendWrappedBuffer);
+          switch (result.getStatus())
+          {
+          case BUFFER_OVERFLOW:
+            // The wrapped buffer is not big enough: resize and repeat.
+            final int newSize = sslEngine.getSession().getPacketBufferSize();
+            final ByteBuffer newSendWrappedBuffer = ByteBuffer
+                .allocate(sendWrappedBuffer.position() + newSize);
+            sendWrappedBuffer.flip();
+            newSendWrappedBuffer.put(sendWrappedBuffer);
+            sendWrappedBuffer = newSendWrappedBuffer;
+            break; // Retry.
+          case BUFFER_UNDERFLOW:
+            // This should not happen for sends.
+            throw new SSLException("Got unexpected underflow while wrapping");
+          case CLOSED:
+            throw new ClosedChannelException();
+          default: // OK
+            // Write the SSL packet: our IO stack will block until all the
+            // data is written.
+            sendWrappedBuffer.flip();
+            while (sendWrappedBuffer.hasRemaining())
+            {
+              channel.write(sendWrappedBuffer);
+            }
+            final int written = sendWrappedBuffer.position();
+            sendWrappedBuffer.clear();
+            return written;
+          }
+        }
+      }
+    }
+
+
+
+    private boolean isHandshaking(final HandshakeStatus status)
+    {
+      return status != HandshakeStatus.NOT_HANDSHAKING;
+    }
+
+  }
+
+
 
   // Map of cipher phrases to effective key size (bits). Taken from the
   // following RFCs: 5289, 4346, 3268,4132 and 4162.
-  private static final Map<String, Integer> cipherMap;
+  private static final Map<String, Integer> CIPHER_MAP;
   static
   {
-    cipherMap = new LinkedHashMap<String, Integer>();
-    cipherMap.put("_WITH_AES_256_CBC_", new Integer(256));
-    cipherMap.put("_WITH_CAMELLIA_256_CBC_", new Integer(256));
-    cipherMap.put("_WITH_AES_256_GCM_", new Integer(256));
-    cipherMap.put("_WITH_3DES_EDE_CBC_", new Integer(112));
-    cipherMap.put("_WITH_AES_128_GCM_", new Integer(128));
-    cipherMap.put("_WITH_SEED_CBC_", new Integer(128));
-    cipherMap.put("_WITH_CAMELLIA_128_CBC_", new Integer(128));
-    cipherMap.put("_WITH_AES_128_CBC_", new Integer(128));
-    cipherMap.put("_WITH_IDEA_CBC_", new Integer(128));
-    cipherMap.put("_WITH_DES_CBC_", new Integer(56));
-    cipherMap.put("_WITH_RC2_CBC_40_", new Integer(40));
-    cipherMap.put("_WITH_RC4_40_", new Integer(40));
-    cipherMap.put("_WITH_DES40_CBC_", new Integer(40));
-    cipherMap.put("_WITH_NULL_", new Integer(0));
-  };
+    CIPHER_MAP = new LinkedHashMap<String, Integer>();
+    CIPHER_MAP.put("_WITH_AES_256_CBC_", new Integer(256));
+    CIPHER_MAP.put("_WITH_CAMELLIA_256_CBC_", new Integer(256));
+    CIPHER_MAP.put("_WITH_AES_256_GCM_", new Integer(256));
+    CIPHER_MAP.put("_WITH_3DES_EDE_CBC_", new Integer(112));
+    CIPHER_MAP.put("_WITH_AES_128_GCM_", new Integer(128));
+    CIPHER_MAP.put("_WITH_SEED_CBC_", new Integer(128));
+    CIPHER_MAP.put("_WITH_CAMELLIA_128_CBC_", new Integer(128));
+    CIPHER_MAP.put("_WITH_AES_128_CBC_", new Integer(128));
+    CIPHER_MAP.put("_WITH_IDEA_CBC_", new Integer(128));
+    CIPHER_MAP.put("_WITH_DES_CBC_", new Integer(56));
+    CIPHER_MAP.put("_WITH_RC2_CBC_40_", new Integer(40));
+    CIPHER_MAP.put("_WITH_RC4_40_", new Integer(40));
+    CIPHER_MAP.put("_WITH_DES40_CBC_", new Integer(40));
+    CIPHER_MAP.put("_WITH_NULL_", new Integer(0));
+  }
+
+  private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
+  private static final DebugTracer TRACER = getTracer();
 
 
 
@@ -124,12 +450,28 @@
 
 
 
+  private final ByteChannelImpl pimpl = new ByteChannelImpl();
+  private final ByteChannel channel;
+  private final SSLEngine sslEngine;
+
+  private ByteBuffer recvWrappedBuffer;
+  private ByteBuffer recvUnwrappedBuffer;
+  private ByteBuffer sendWrappedBuffer;
+
+  private final Object handshakeLock = new Object();
+  private final Object unwrapLock = new Object();
+  private final Object wrapLock = new Object();
+  private final Object readLock = new Object();
+  private final Object writeLock = new Object();
+
+
+
   private TLSByteChannel(final LDAPConnectionHandlerCfg config,
-      final ClientConnection c, final ByteChannel socketChannel,
+      final ClientConnection c, final ByteChannel channel,
       final SSLContext sslContext)
   {
 
-    this.socketChannel = socketChannel;
+    this.channel = channel;
 
     // getHostName could potentially be very expensive and could block
     // the connection handler for several minutes. (See issue 4229)
@@ -137,11 +479,14 @@
     // avoid blocking new connections. Just remove for now to prevent
     // potential DoS attacks. SSL sessions will not be reused and some
     // cipher suites (such as Kerberos) will not work.
+
     // String hostName = socketChannel.socket().getInetAddress().getHostName();
     // int port = socketChannel.socket().getPort();
     // sslEngine = sslContext.createSSLEngine(hostName, port);
+
     sslEngine = sslContext.createSSLEngine();
     sslEngine.setUseClientMode(false);
+
     final Set<String> protocols = config.getSSLProtocol();
     if (!protocols.isEmpty())
     {
@@ -171,15 +516,18 @@
       break;
     }
 
-    final SSLSession sslSession = sslEngine.getSession();
-    sslBufferSize = sslSession.getPacketBufferSize();
-    appBufSize = sslSession.getApplicationBufferSize();
+    // Allocate read/write buffers.
+    final SSLSession session = sslEngine.getSession();
+    final int wrappedBufferSize = session.getPacketBufferSize();
+    final int unwrappedBufferSize = session.getApplicationBufferSize();
 
-    appNetData = ByteBuffer.allocate(sslBufferSize);
-    netData = ByteBuffer.allocate(sslBufferSize);
+    sendWrappedBuffer = ByteBuffer.allocate(wrappedBufferSize);
+    recvWrappedBuffer = ByteBuffer.allocate(wrappedBufferSize);
+    recvUnwrappedBuffer = ByteBuffer.allocate(unwrappedBufferSize);
 
-    appData = ByteBuffer.allocate(sslSession.getApplicationBufferSize());
-    tempData = ByteBuffer.allocate(sslSession.getApplicationBufferSize());
+    // Initially nothing has been received.
+    recvWrappedBuffer.flip();
+    recvUnwrappedBuffer.flip();
   }
 
 
@@ -187,27 +535,9 @@
   /**
    * {@inheritDoc}
    */
-  public synchronized void close() throws IOException
+  public ByteChannel getChannel()
   {
-    sslEngine.closeInbound();
-    sslEngine.closeOutbound();
-    final SSLEngineResult.HandshakeStatus hsStatus = sslEngine
-        .getHandshakeStatus();
-    if (hsStatus != SSLEngineResult.HandshakeStatus.FINISHED
-        && hsStatus != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
-    {
-      doHandshakeWrite(hsStatus);
-    }
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public int getAppBufSize()
-  {
-    return appBufSize;
+    return pimpl;
   }
 
 
@@ -248,31 +578,15 @@
    */
   public int getSSF()
   {
-    int cipherKeySSF = 0;
     final String cipherString = sslEngine.getSession().getCipherSuite();
-    for (final Map.Entry<String, Integer> mapEntry : cipherMap.entrySet())
+    for (final Map.Entry<String, Integer> mapEntry : CIPHER_MAP.entrySet())
     {
       if (cipherString.indexOf(mapEntry.getKey()) >= 0)
       {
-        cipherKeySSF = mapEntry.getValue().intValue();
-        break;
+        return mapEntry.getValue().intValue();
       }
     }
-    return cipherKeySSF;
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean isOpen()
-  {
-    if (sslEngine.isInboundDone() || sslEngine.isOutboundDone())
-    {
-      return false;
-    }
-    return true;
+    return 0;
   }
 
 
@@ -285,269 +599,4 @@
     return true;
   }
 
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public synchronized int read(final ByteBuffer clearBuffer) throws IOException
-  {
-    SSLEngineResult.HandshakeStatus hsStatus;
-    if (!reading)
-    {
-      appNetData.clear();
-    }
-    else
-    {
-      reading = false;
-    }
-
-    if (!socketChannel.isOpen())
-    {
-      return -1;
-    }
-
-    if (sslEngine.isInboundDone())
-    {
-      return -1;
-    }
-
-    do
-    {
-      final int wrappedBytes = socketChannel.read(appNetData);
-      appNetData.flip();
-
-      if (wrappedBytes == -1)
-      {
-        return -1;
-      }
-
-      hsStatus = sslEngine.getHandshakeStatus();
-
-      if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK
-          || hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP)
-      {
-        doHandshakeRead(hsStatus);
-      }
-
-      if (wrappedBytes == 0)
-      {
-        return 0;
-      }
-
-      while (appNetData.hasRemaining())
-      {
-        appData.clear();
-        final SSLEngineResult res = sslEngine.unwrap(appNetData, appData);
-        appData.flip();
-
-        if (res.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW)
-        {
-          appNetData.compact();
-          reading = true;
-          break;
-        }
-        else if (res.getStatus() != SSLEngineResult.Status.OK)
-        {
-          return -1;
-        }
-
-        hsStatus = sslEngine.getHandshakeStatus();
-        if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK
-            || hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP)
-        {
-          doHandshakeOp(hsStatus);
-        }
-        clearBuffer.put(appData);
-      }
-      hsStatus = sslEngine.getHandshakeStatus();
-    }
-    while (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK
-        || hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP);
-
-    return clearBuffer.position();
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public ByteChannel wrapChannel(final ByteChannel channel)
-  {
-    return this;
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public synchronized int write(final ByteBuffer clearData) throws IOException
-  {
-    if (!socketChannel.isOpen() || sslEngine.isOutboundDone())
-    {
-      throw new ClosedChannelException();
-    }
-
-    final int originalPosition = clearData.position();
-    final int originalLimit = clearData.limit();
-    final int length = originalLimit - originalPosition;
-
-    if (length > sslBufferSize)
-    {
-      int pos = originalPosition;
-      int lim = originalPosition + sslBufferSize;
-      while (pos < originalLimit)
-      {
-        clearData.position(pos);
-        clearData.limit(lim);
-        writeInternal(clearData);
-        pos = lim;
-        lim = Math.min(originalLimit, pos + sslBufferSize);
-      }
-      return length;
-    }
-    else
-    {
-      return writeInternal(clearData);
-    }
-  }
-
-
-
-  private void doHandshakeOp(SSLEngineResult.HandshakeStatus hsStatus)
-      throws IOException
-  {
-    SSLEngineResult res;
-    switch (hsStatus)
-    {
-    case NEED_TASK:
-      hsStatus = doTasks();
-      break;
-    case NEED_WRAP:
-      tempData.clear();
-      netData.clear();
-      res = sslEngine.wrap(tempData, netData);
-      hsStatus = res.getHandshakeStatus();
-      netData.flip();
-      while (netData.hasRemaining())
-      {
-        socketChannel.write(netData);
-      }
-      hsStatus = sslEngine.getHandshakeStatus();
-      return;
-    default:
-      return;
-    }
-  }
-
-
-
-  private void doHandshakeRead(SSLEngineResult.HandshakeStatus hsStatus)
-      throws IOException
-  {
-    do
-    {
-      doHandshakeOp(hsStatus);
-      hsStatus = sslEngine.getHandshakeStatus();
-    }
-    while (hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP
-        || hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK);
-  }
-
-
-
-  private void doHandshakeUnwrap() throws IOException
-  {
-    netData.clear();
-    tempData.clear();
-    final int bytesRead = socketChannel.read(netData);
-
-    if (bytesRead <= 0)
-    {
-      throw new ClosedChannelException();
-    }
-    else
-    {
-      sslEngine.unwrap(netData, tempData);
-    }
-  }
-
-
-
-  private void doHandshakeWrite(SSLEngineResult.HandshakeStatus hsStatus)
-      throws IOException
-  {
-    do
-    {
-      if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)
-      {
-        doHandshakeUnwrap();
-      }
-      else
-      {
-        doHandshakeOp(hsStatus);
-      }
-      hsStatus = sslEngine.getHandshakeStatus();
-    }
-    while (hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP
-        || hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK
-        || hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP);
-  }
-
-
-
-  private SSLEngineResult.HandshakeStatus doTasks()
-  {
-    Runnable task;
-    while ((task = sslEngine.getDelegatedTask()) != null)
-    {
-      task.run();
-    }
-    return sslEngine.getHandshakeStatus();
-  }
-
-
-
-  private int writeInternal(final ByteBuffer clearData) throws IOException
-  {
-    int totBytesSent = 0;
-    SSLEngineResult.HandshakeStatus hsStatus;
-    hsStatus = sslEngine.getHandshakeStatus();
-
-    if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK
-        || hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP
-        || hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)
-    {
-      doHandshakeWrite(hsStatus);
-    }
-
-    while (clearData.hasRemaining())
-    {
-      netData.clear();
-      final SSLEngineResult res = sslEngine.wrap(clearData, netData);
-      netData.flip();
-      if (netData.remaining() == 0)
-      {
-        // wrap didn't produce any data from our clear buffer.
-        // Throw exception to prevent looping.
-        throw new SSLException("SSLEngine.wrap produced 0 bytes");
-      }
-
-      if (res.getStatus() != SSLEngineResult.Status.OK)
-      {
-        throw new ClosedChannelException();
-      }
-
-      if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK
-          || hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP
-          || hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)
-      {
-        doHandshakeWrite(hsStatus);
-      }
-      totBytesSent += socketChannel.write(netData);
-    }
-    return totBytesSent;
-  }
 }

--
Gitblit v1.10.0