From 88be99a38d4f02a6227ef5a2b514f77c6f28e524 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Wed, 08 Feb 2012 10:59:12 +0000
Subject: [PATCH] Preparation work for OPENDJ-420: Rare SSLExceptions while handling LDAPS connections and big LDAP searches

---
 opends/src/server/org/opends/server/extensions/TLSByteChannel.java |  814 ++++++++++++++++++++++++++++++++++-----------------------
 1 files changed, 482 insertions(+), 332 deletions(-)

diff --git a/opends/src/server/org/opends/server/extensions/TLSByteChannel.java b/opends/src/server/org/opends/server/extensions/TLSByteChannel.java
index ff85170..ad2b96e 100644
--- a/opends/src/server/org/opends/server/extensions/TLSByteChannel.java
+++ b/opends/src/server/org/opends/server/extensions/TLSByteChannel.java
@@ -23,9 +23,12 @@
  *
  *
  *      Copyright 2008-2009 Sun Microsystems, Inc.
+ *      Portions copyright 2012 ForgeRock AS.
  */
 package org.opends.server.extensions;
 
+
+
 import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
 import static org.opends.server.loggers.debug.DebugLogger.getTracer;
 
@@ -45,359 +48,506 @@
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.types.DebugLogLevel;
 
+
+
 /**
  * A class that provides a TLS byte channel implementation.
- *
  */
-public class TLSByteChannel implements
-                                ByteChannel, ConnectionSecurityProvider {
+public class TLSByteChannel implements ByteChannel, ConnectionSecurityProvider
+{
+  private static final DebugTracer TRACER = getTracer();
 
-    private static final DebugTracer TRACER = getTracer();
+  private final ByteChannel socketChannel;
 
-    private final ClientConnection connection;
-    private final ByteChannel socketChannel;
+  private final SSLEngine sslEngine;
 
-    private final SSLEngine sslEngine;
+  // read copy to buffer
+  private final ByteBuffer appData;
 
-    //read copy to buffer
-    private final ByteBuffer appData;
-    //read encrypted
-    private final ByteBuffer appNetData;
+  // read encrypted
+  private final ByteBuffer appNetData;
 
-    //Write encrypted
-    private final ByteBuffer netData, tempData;
-    private final int sslBufferSize, appBufSize;
-    private boolean reading = false;
+  // Write encrypted
+  private final ByteBuffer netData;
+  private final ByteBuffer tempData;
 
-    //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 final int sslBufferSize;
+  private final int appBufSize;
 
-    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));
-    };
+  private boolean reading = false;
 
-    private TLSByteChannel(LDAPConnectionHandlerCfg config, ClientConnection c,
-        ByteChannel socketChannel, SSLContext sslContext)  {
+  // 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;
+  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));
+  };
 
-        this.socketChannel = socketChannel;
-        this.connection = c;
-      // getHostName could potentially be very expensive and could block
-      // the connection handler for several minutes. (See issue 4229)
-      // Accepting new connections should be done in a seperate thread to
-      // 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);
-        Set<String> protocols = config.getSSLProtocol();
-        if (!protocols.isEmpty())
-            sslEngine.setEnabledProtocols(protocols.toArray(new String[0]));
-        Set<String> ciphers = config.getSSLCipherSuite();
-        if (!ciphers.isEmpty())
-            sslEngine.setEnabledCipherSuites(ciphers.toArray(new String[0]));
-        switch (config.getSSLClientAuthPolicy()) {
-        case DISABLED:
-            sslEngine.setNeedClientAuth(false);
-            sslEngine.setWantClientAuth(false);
-            break;
-        case REQUIRED:
-            sslEngine.setWantClientAuth(true);
-            sslEngine.setNeedClientAuth(true);
-            break;
-        case OPTIONAL:
-        default:
-            sslEngine.setNeedClientAuth(false);
-            sslEngine.setWantClientAuth(true);
-            break;
+
+
+  /**
+   * Create an TLS byte channel instance using the specified LDAP connection
+   * configuration, client connection, SSL context and socket channel
+   * parameters.
+   *
+   * @param config
+   *          The LDAP connection configuration.
+   * @param c
+   *          The client connection.
+   * @param sslContext
+   *          The SSL context.
+   * @param socketChannel
+   *          The socket channel.
+   * @return A TLS capable byte channel.
+   */
+  public static TLSByteChannel getTLSByteChannel(
+      final LDAPConnectionHandlerCfg config, final ClientConnection c,
+      final SSLContext sslContext, final ByteChannel socketChannel)
+  {
+    return new TLSByteChannel(config, c, socketChannel, sslContext);
+  }
+
+
+
+  private TLSByteChannel(final LDAPConnectionHandlerCfg config,
+      final ClientConnection c, final ByteChannel socketChannel,
+      final SSLContext sslContext)
+  {
+
+    this.socketChannel = socketChannel;
+
+    // getHostName could potentially be very expensive and could block
+    // the connection handler for several minutes. (See issue 4229)
+    // Accepting new connections should be done in a seperate thread to
+    // 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())
+    {
+      sslEngine.setEnabledProtocols(protocols.toArray(new String[0]));
+    }
+
+    final Set<String> ciphers = config.getSSLCipherSuite();
+    if (!ciphers.isEmpty())
+    {
+      sslEngine.setEnabledCipherSuites(ciphers.toArray(new String[0]));
+    }
+
+    switch (config.getSSLClientAuthPolicy())
+    {
+    case DISABLED:
+      sslEngine.setNeedClientAuth(false);
+      sslEngine.setWantClientAuth(false);
+      break;
+    case REQUIRED:
+      sslEngine.setWantClientAuth(true);
+      sslEngine.setNeedClientAuth(true);
+      break;
+    case OPTIONAL:
+    default:
+      sslEngine.setNeedClientAuth(false);
+      sslEngine.setWantClientAuth(true);
+      break;
+    }
+
+    final SSLSession sslSession = sslEngine.getSession();
+    sslBufferSize = sslSession.getPacketBufferSize();
+    appBufSize = sslSession.getApplicationBufferSize();
+
+    appNetData = ByteBuffer.allocate(sslBufferSize);
+    netData = ByteBuffer.allocate(sslBufferSize);
+
+    appData = ByteBuffer.allocate(sslSession.getApplicationBufferSize());
+    tempData = ByteBuffer.allocate(sslSession.getApplicationBufferSize());
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public synchronized void close() throws IOException
+  {
+    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;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public Certificate[] getClientCertificateChain()
+  {
+    try
+    {
+      return sslEngine.getSession().getPeerCertificates();
+    }
+    catch (final SSLPeerUnverifiedException e)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+      return new Certificate[0];
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public String getName()
+  {
+    return "TLS";
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public int getSSF()
+  {
+    int cipherKeySSF = 0;
+    final String cipherString = sslEngine.getSession().getCipherSuite();
+    for (final Map.Entry<String, Integer> mapEntry : cipherMap.entrySet())
+    {
+      if (cipherString.indexOf(mapEntry.getKey()) >= 0)
+      {
+        cipherKeySSF = mapEntry.getValue().intValue();
+        break;
+      }
+    }
+    return cipherKeySSF;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isOpen()
+  {
+    if (sslEngine.isInboundDone() || sslEngine.isOutboundDone())
+    {
+      return false;
+    }
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isSecure()
+  {
+    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;
         }
-        SSLSession sslSession = sslEngine.getSession();
-        sslBufferSize = sslSession.getPacketBufferSize();
-        appBufSize = sslSession.getApplicationBufferSize();
-
-        appNetData = ByteBuffer.allocate(sslBufferSize);
-        netData = ByteBuffer.allocate(sslBufferSize);
-
-        appData = ByteBuffer.allocate(sslSession.getApplicationBufferSize());
-        tempData = ByteBuffer.allocate(sslSession.getApplicationBufferSize());
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public int getAppBufSize() {
-        return appBufSize;
-    }
-
-    /**
-     * Create an TLS byte channel instance using the specified LDAP connection
-     * configuration, client connection, SSL context and socket channel
-     * parameters.
-     *
-     * @param config The LDAP connection configuration.
-     * @param c The client connection.
-     * @param sslContext The SSL context.
-     * @param socketChannel The socket channel.
-     * @return A TLS capable byte channel.
-     */
-    public static TLSByteChannel
-    getTLSByteChannel(LDAPConnectionHandlerCfg config, ClientConnection c,
-                        SSLContext sslContext, ByteChannel socketChannel) {
-        return new TLSByteChannel(config, c, socketChannel, sslContext);
-    }
-
-    private SSLEngineResult.HandshakeStatus doTasks() {
-        Runnable task;
-        while ((task = sslEngine.getDelegatedTask()) != null)
-            task.run();
-        return sslEngine.getHandshakeStatus();
-    }
-
-    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  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;
+        else if (res.getStatus() != SSLEngineResult.Status.OK)
+        {
+          return -1;
         }
-    }
 
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized int read(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 {
-            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();
-                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 synchronized void close() throws IOException {
-        sslEngine.closeInbound();
-        sslEngine.closeOutbound();
-        SSLEngineResult.HandshakeStatus hsStatus =
-                                      sslEngine.getHandshakeStatus();
-        if(hsStatus != SSLEngineResult.HandshakeStatus.FINISHED &&
-           hsStatus !=  SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
-            doHandshakeWrite(hsStatus);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public boolean isOpen() {
-        if(sslEngine.isInboundDone() || sslEngine.isOutboundDone())
-          return false;
-        return true;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public int getSSF() {
-        int cipherKeySSF = 0;
-        String cipherString = sslEngine.getSession().getCipherSuite();
-        for(Map.Entry<String, Integer> mapEntry : cipherMap.entrySet()) {
-            if(cipherString.indexOf(mapEntry.getKey()) >= 0) {
-                cipherKeySSF = mapEntry.getValue().intValue();
-                break;
-            }
-        }
-        return cipherKeySSF;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public  Certificate[] getClientCertificateChain() {
-        try {
-          return sslEngine.getSession().getPeerCertificates();
-        }
-        catch (SSLPeerUnverifiedException e) {
-          if (debugEnabled()) {
-            TRACER.debugCaught(DebugLogLevel.ERROR, e);
-          }
-          return new Certificate[0];
-        }
-    }
-
-    private void doHandshakeUnwrap() throws IOException {
-        netData.clear();
-        tempData.clear();
-        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);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized int write(ByteBuffer clearData) throws IOException {
-        if(!socketChannel.isOpen() || sslEngine.isOutboundDone()) {
-            throw new ClosedChannelException();
-        }
-        int originalPosition = clearData.position();
-        int originalLimit = clearData.limit();
-        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 int writeInternal(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();
-            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);
+        if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK
+            || hsStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP)
+        {
+          doHandshakeOp(hsStatus);
         }
-        return totBytesSent;
+        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();
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    public ByteChannel wrapChannel(ByteChannel channel) {
-        return this;
+    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);
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    public String getName() {
-        return "TLS";
-    }
+    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");
+      }
 
-    /**
-     * {@inheritDoc}
-     */
-    public boolean isSecure() {
-        return true;
+      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