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 ++++++++++++++++++------------
 opends/src/server/org/opends/server/extensions/SASLByteChannel.java |  708 +++++++++++++++-----------
 2 files changed, 885 insertions(+), 637 deletions(-)

diff --git a/opends/src/server/org/opends/server/extensions/SASLByteChannel.java b/opends/src/server/org/opends/server/extensions/SASLByteChannel.java
index f7749d8..03ab350 100644
--- a/opends/src/server/org/opends/server/extensions/SASLByteChannel.java
+++ b/opends/src/server/org/opends/server/extensions/SASLByteChannel.java
@@ -28,354 +28,452 @@
 
 package org.opends.server.extensions;
 
-import java.nio.channels.ByteChannel;
-import java.security.cert.Certificate;
+
+
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.nio.channels.ByteChannel;
+import java.security.cert.Certificate;
+
 import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
+
 import org.opends.server.api.ClientConnection;
 
+
+
 /**
  * This class implements a SASL byte channel that can be used during
  * confidentiality and integrity.
- *
  */
-public class
-SASLByteChannel implements ByteChannel, ConnectionSecurityProvider {
+public class SASLByteChannel implements ByteChannel, ConnectionSecurityProvider
+{
 
-    // The client connection associated with this provider.
-    private ClientConnection connection;
+  /**
+   * Return a SASL byte channel instance created using the specified parameters.
+   *
+   * @param c
+   *          A client connection associated with the instance.
+   * @param name
+   *          The name of the instance (SASL mechanism name).
+   * @param context
+   *          A SASL context associated with the instance.
+   * @return A SASL byte channel.
+   */
+  public static SASLByteChannel getSASLByteChannel(final ClientConnection c,
+      final String name, final SASLContext context)
+  {
+    return new SASLByteChannel(c, name, context);
+  }
 
-    // The SASL context associated with the provider
-    private SASLContext saslContext;
 
-    // The byte channel associated with this provider.
-    private RedirectingByteChannel channel;
 
-    // The number of bytes in the length buffer.
-    private static final int lengthSize = 4;
+  // The SASL context associated with the provider
+  private SASLContext saslContext;
 
-    //Length of the buffer.
-    private int bufLength;
+  // The byte channel associated with this provider.
+  private final RedirectingByteChannel channel;
 
-    // The SASL mechanism name.
-    private String name;
+  // The number of bytes in the length buffer.
+  private static final int lengthSize = 4;
 
-    //Buffers used in reading and decoding (unwrap)
-    private ByteBuffer readBuffer, decodeBuffer;
+  // Length of the buffer.
+  private int bufLength;
 
-    //How many bytes of the subsequent buffer is needed to complete a partially
-    //read buffer.
-    private int neededBytes = 0;
+  // The SASL mechanism name.
+  private final String name;
 
-    //Used to not reset the buffer length size because the first 4 bytes of a
-    //buffer are not size bytes.
-    private boolean reading = false;
+  // Buffers used in reading and decoding (unwrap)
+  private final ByteBuffer readBuffer, decodeBuffer;
 
-    /**
-     * Create a SASL byte channel with the specified parameters
-     * that is capable of processing a confidentiality/integrity SASL
-     * connection.
-     *
-     * @param connection
-     *          The client connection to read/write the bytes.
-     * @param name
-     *          The SASL mechanism name.
-     * @param saslContext
-     *          The SASL context to process the data through.
-     */
-    private SASLByteChannel(ClientConnection connection, String name,
-        SASLContext saslContext) {
-      this.connection = connection;
-      this.name = name;
-      this.saslContext = saslContext;
-      this.channel = connection.getChannel();
-      this.readBuffer = ByteBuffer.allocate(connection.getAppBufferSize());
-      this.decodeBuffer =
-                ByteBuffer.allocate(connection.getAppBufferSize() + lengthSize);
+  // How many bytes of the subsequent buffer is needed to complete a partially
+  // read buffer.
+  private int neededBytes = 0;
+
+  // Used to not reset the buffer length size because the first 4 bytes of a
+  // buffer are not size bytes.
+  private boolean reading = false;
+
+
+
+  /**
+   * Create a SASL byte channel with the specified parameters that is capable of
+   * processing a confidentiality/integrity SASL connection.
+   *
+   * @param connection
+   *          The client connection to read/write the bytes.
+   * @param name
+   *          The SASL mechanism name.
+   * @param saslContext
+   *          The SASL context to process the data through.
+   */
+  private SASLByteChannel(final ClientConnection connection, final String name,
+      final SASLContext saslContext)
+  {
+    this.name = name;
+    this.saslContext = saslContext;
+    this.channel = connection.getChannel();
+    this.readBuffer = ByteBuffer.allocate(connection.getAppBufferSize());
+    this.decodeBuffer = ByteBuffer.allocate(connection.getAppBufferSize()
+        + lengthSize);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public synchronized void close() throws IOException
+  {
+    saslContext.dispose();
+    saslContext = null;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int getAppBufSize()
+  {
+    return saslContext.getBufSize(Sasl.MAX_BUFFER);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Certificate[] getClientCertificateChain()
+  {
+    return new Certificate[0];
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String getName()
+  {
+    return name;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int getSSF()
+  {
+    return saslContext.getSSF();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean isOpen()
+  {
+    return saslContext != null;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean isSecure()
+  {
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public synchronized int read(final ByteBuffer clearDst) throws IOException
+  {
+    int bytesToRead = lengthSize;
+    if (reading)
+    {
+      bytesToRead = neededBytes;
     }
 
-    /**
-     * Return a SASL byte channel instance created using the specified
-     * parameters.
-     *
-     * @param c A client connection associated with the instance.
-     * @param name The name of the instance (SASL mechanism name).
-     * @param context A SASL context associated with the instance.
-     * @return A SASL byte channel.
-     */
-    public static SASLByteChannel
-    getSASLByteChannel(ClientConnection c, String name,
-                          SASLContext context) {
-          return new SASLByteChannel(c, name, context);
+    final int readResult = readAll(readBuffer, bytesToRead);
+    if (readResult == -1)
+    {
+      return -1;
     }
 
-    /**
-     * Finish processing a previous, partially read buffer using some, or, all
-     * of the bytes of the current buffer.
-     *
-     */
-    private int processPartial(int readResult, ByteBuffer clearDst)
-    throws IOException {
+    // The previous buffer read was not complete, the current
+    // buffer completes it.
+    if (neededBytes > 0 && readResult > 0)
+    {
+      return (processPartial(readResult, clearDst));
+    }
+
+    if (readResult == 0 && !reading)
+    {
+      return 0;
+    }
+
+    if (!reading)
+    {
+      bufLength = getBufLength(readBuffer);
+    }
+
+    reading = false;
+
+    // The buffer length is greater than what is there, save what is there,
+    // figure out how much more is needed and return.
+    if (bufLength > readBuffer.position())
+    {
+      neededBytes = bufLength - readBuffer.position() + lengthSize;
       readBuffer.flip();
-      //Use all of the bytes of the current buffer and read some more.
-      if(neededBytes > readResult) {
-        neededBytes -= readResult;
-        decodeBuffer.put(readBuffer);
-        readBuffer.clear();
-        reading = false;
-        return 0;
-      }
-      //Use a portion of the current buffer.
-      for(;neededBytes > 0;neededBytes--) {
-        decodeBuffer.put(readBuffer.get());
-      }
-      //Unwrap the now completed buffer.
-      byte[] inBytes = decodeBuffer.array();
-      byte[]clearBytes = saslContext.unwrap(inBytes, lengthSize, bufLength);
-      clearDst.put(clearBytes);
+      decodeBuffer.put(readBuffer);
+      readBuffer.clear();
+      return 0;
+    }
+    else
+    {
+      readBuffer.flip();
+      decodeBuffer.put(readBuffer);
+      final byte[] inBytes = decodeBuffer.array();
+      final byte[] clearBytes = saslContext.unwrap(inBytes, lengthSize,
+          bufLength);
       decodeBuffer.clear();
-      readBuffer.compact();
-      //If the read buffer has bytes, these are a new buffer. Reset the
-      //buffer length to the new value.
-      if(readBuffer.position() != 0) {
-        bufLength = getBufLength(readBuffer);
-        reading = true;
-      } else
-        reading=false;
+      clearDst.put(clearBytes);
+      readBuffer.clear();
       return clearDst.position();
     }
+  }
 
-    /**
-     * Read from the socket channel into the specified byte buffer at least
-     * the number of bytes specified in the total parameter.
-     *
-     * @param byteBuf
-     *          The byte buffer to put the bytes in.
-     * @param total
-     *          The total number of bytes to read from the socket
-     *          channel.
-     * @return The number of bytes read, 0 or -1.
-     * @throws IOException
-     *           If an error occurred reading the socket channel.
-     */
-    private int readAll(ByteBuffer byteBuf, int total) throws IOException
-    {
-      while (channel.isOpen() && total > 0) {
-        int count = channel.read(byteBuf);
-        if (count == -1) return -1;
-        if (count == 0) return 0;
-        total -= count;
-      }
-      if (total > 0)
-        return -1;
-      else
-        return byteBuf.position();
-    }
 
-    /**
-     * Return the clear buffer length as determined by processing the
-     * first 4 bytes of the specified buffer.
-     *
-     * @param byteBuf
-     *          The buffer to examine the first 4 bytes of.
-     * @return The size of the clear buffer.
-     */
-    private int getBufLength(ByteBuffer byteBuf)
-    {
-      int answer = 0;
 
-      for (int i = 0; i < lengthSize; i++)
-      {
-        byte b = byteBuf.get(i);
-        answer <<= 8;
-        answer |= ((int) b & 0xff);
-      }
-      return answer;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
+  /**
+   * {@inheritDoc}
+   */
   @Override
-    public synchronized int read(ByteBuffer clearDst) throws IOException {
-      int bytesToRead = lengthSize;
-      if(reading)
-        bytesToRead = neededBytes;
-      int readResult = readAll(readBuffer, bytesToRead);
-      if (readResult == -1)
-        return -1;
-      //The previous buffer read was not complete, the current
-      //buffer completes it.
-      if(neededBytes > 0 && readResult > 0)
-          return(processPartial(readResult, clearDst));
-      if(readResult == 0 && !reading) return 0;
-      if(!reading) {
-        bufLength = getBufLength(readBuffer);
+  public ByteChannel wrapChannel(final ByteChannel channel)
+  {
+    return this;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public synchronized int write(final ByteBuffer clearSrc) throws IOException
+  {
+    final int sendBufSize = getAppBufSize();
+    final int srcLen = clearSrc.remaining();
+    final ByteBuffer sendBuffer = ByteBuffer.allocate(sendBufSize);
+
+    if (srcLen > sendBufSize)
+    {
+      final int oldPos = clearSrc.position();
+      int curPos = oldPos;
+      int curLimit = oldPos + sendBufSize;
+
+      while (curPos < srcLen)
+      {
+        clearSrc.position(curPos);
+        clearSrc.limit(curLimit);
+        sendBuffer.put(clearSrc);
+        writeChannel(wrap(sendBuffer.array(), clearSrc.remaining()));
+        curPos = curLimit;
+        curLimit = Math.min(srcLen, curPos + sendBufSize);
       }
-      reading=false;
-      //The buffer length is greater than what is there, save what is there,
-      //figure out how much more is needed and return.
-      if(bufLength > readBuffer.position()) {
-        neededBytes = bufLength - readBuffer.position() + lengthSize;
-        readBuffer.flip();
-        decodeBuffer.put(readBuffer);
-        readBuffer.clear();
+      return srcLen;
+    }
+    else
+    {
+      sendBuffer.put(clearSrc);
+      return writeChannel(wrap(sendBuffer.array(), srcLen));
+    }
+  }
+
+
+
+  /**
+   * Return the clear buffer length as determined by processing the first 4
+   * bytes of the specified buffer.
+   *
+   * @param byteBuf
+   *          The buffer to examine the first 4 bytes of.
+   * @return The size of the clear buffer.
+   */
+  private int getBufLength(final ByteBuffer byteBuf)
+  {
+    int answer = 0;
+    for (int i = 0; i < lengthSize; i++)
+    {
+      final byte b = byteBuf.get(i);
+      answer <<= 8;
+      answer |= (b & 0xff);
+    }
+    return answer;
+  }
+
+
+
+  /**
+   * Finish processing a previous, partially read buffer using some, or, all of
+   * the bytes of the current buffer.
+   */
+  private int processPartial(final int readResult, final ByteBuffer clearDst)
+      throws IOException
+  {
+    readBuffer.flip();
+
+    // Use all of the bytes of the current buffer and read some more.
+    if (neededBytes > readResult)
+    {
+      neededBytes -= readResult;
+      decodeBuffer.put(readBuffer);
+      readBuffer.clear();
+      reading = false;
+      return 0;
+    }
+
+    // Use a portion of the current buffer.
+    for (; neededBytes > 0; neededBytes--)
+    {
+      decodeBuffer.put(readBuffer.get());
+    }
+
+    // Unwrap the now completed buffer.
+    final byte[] inBytes = decodeBuffer.array();
+    final byte[] clearBytes = saslContext
+        .unwrap(inBytes, lengthSize, bufLength);
+    clearDst.put(clearBytes);
+    decodeBuffer.clear();
+    readBuffer.compact();
+
+    // If the read buffer has bytes, these are a new buffer. Reset the
+    // buffer length to the new value.
+    if (readBuffer.position() != 0)
+    {
+      bufLength = getBufLength(readBuffer);
+      reading = true;
+    }
+    else
+    {
+      reading = false;
+    }
+    return clearDst.position();
+  }
+
+
+
+  /**
+   * Read from the socket channel into the specified byte buffer at least the
+   * number of bytes specified in the total parameter.
+   *
+   * @param byteBuf
+   *          The byte buffer to put the bytes in.
+   * @param total
+   *          The total number of bytes to read from the socket channel.
+   * @return The number of bytes read, 0 or -1.
+   * @throws IOException
+   *           If an error occurred reading the socket channel.
+   */
+  private int readAll(final ByteBuffer byteBuf, int total) throws IOException
+  {
+    while (channel.isOpen() && total > 0)
+    {
+      final int count = channel.read(byteBuf);
+      if (count == -1)
+      {
+        return -1;
+      }
+      if (count == 0)
+      {
         return 0;
-      } else {
-        readBuffer.flip();
-        decodeBuffer.put(readBuffer);
-        byte[] inBytes = decodeBuffer.array();
-        byte[]clearBytes = saslContext.unwrap(inBytes, lengthSize, bufLength);
-        decodeBuffer.clear();
-        clearDst.put(clearBytes);
-        readBuffer.clear();
       }
-      return clearDst.position();
+      total -= count;
     }
-
-    /**
-     * Writes the specified len parameter into the buffer in a form that
-     * can be sent over a network to the client.
-     *
-     * @param buf
-     *          The buffer to hold the length bytes.
-     * @param len
-     *          The length to encode.
-     */
-    private void writeBufLen(byte[] buf, int len)
+    if (total > 0)
     {
-      for (int i = 3; i >= 0; i--)
-      {
-        buf[i] = (byte) (len & 0xff);
-        len >>>= 8;
-      }
+      return -1;
     }
-
-    /**
-     * Creates a buffer suitable to send to the client using the
-     * specified clear byte array  and length of the bytes to
-     * wrap.
-     *
-     * @param clearBytes
-     *          The clear byte array to send to the client.
-     * @param len
-     *          The length of the bytes to wrap in the byte array.
-     * @throws SaslException
-     *           If the wrap of the bytes fails.
-     */
-    private ByteBuffer wrap(byte[] clearBytes, int len) throws IOException {
-      byte[] wrapBytes = saslContext.wrap(clearBytes, 0, len);
-      byte[] outBytes = new byte[wrapBytes.length + lengthSize];
-      writeBufLen(outBytes, wrapBytes.length);
-      System.arraycopy(wrapBytes, 0, outBytes, lengthSize, wrapBytes.length);
-      return ByteBuffer.wrap(outBytes);
+    else
+    {
+      return byteBuf.position();
     }
+  }
 
 
-    /**
-     * {@inheritDoc}
-     */
-  @Override
-    public synchronized int write(ByteBuffer clearSrc) throws IOException {
-        int sendBufSize = getAppBufSize();
-        int srcLen = clearSrc.remaining();
-        ByteBuffer sendBuffer = ByteBuffer.allocate(sendBufSize);
-        if (srcLen > sendBufSize) {
-            int oldPos = clearSrc.position();
-            int curPos = oldPos;
-            int curLimit = oldPos + sendBufSize;
-            while (curPos < srcLen) {
-                clearSrc.position(curPos);
-                clearSrc.limit(curLimit);
-                sendBuffer.put(clearSrc);
-                writeChannel(wrap(sendBuffer.array(), clearSrc.remaining()));
-                curPos = curLimit;
-                curLimit = Math.min(srcLen, curPos + sendBufSize);
-            }
-            return srcLen;
-        } else {
-            sendBuffer.put(clearSrc);
-            return writeChannel(wrap(sendBuffer.array() ,srcLen));
-        }
+
+  /**
+   * Creates a buffer suitable to send to the client using the specified clear
+   * byte array and length of the bytes to wrap.
+   *
+   * @param clearBytes
+   *          The clear byte array to send to the client.
+   * @param len
+   *          The length of the bytes to wrap in the byte array.
+   * @throws SaslException
+   *           If the wrap of the bytes fails.
+   */
+  private ByteBuffer wrap(final byte[] clearBytes, final int len)
+      throws SaslException
+  {
+    final byte[] wrapBytes = saslContext.wrap(clearBytes, 0, len);
+    final byte[] outBytes = new byte[wrapBytes.length + lengthSize];
+
+    writeBufLen(outBytes, wrapBytes.length);
+    System.arraycopy(wrapBytes, 0, outBytes, lengthSize, wrapBytes.length);
+
+    return ByteBuffer.wrap(outBytes);
+  }
+
+
+
+  /**
+   * Writes the specified len parameter into the buffer in a form that can be
+   * sent over a network to the client.
+   *
+   * @param buf
+   *          The buffer to hold the length bytes.
+   * @param len
+   *          The length to encode.
+   */
+  private void writeBufLen(final byte[] buf, int len)
+  {
+    for (int i = 3; i >= 0; i--)
+    {
+      buf[i] = (byte) (len & 0xff);
+      len >>>= 8;
     }
+  }
 
 
-    /**
-     * Write the specified byte buffer to the socket channel.
-     *
-     * @param buffer
-     *          The byte buffer to write to the socket channel.
-     * @return {@code true} if the byte buffer was successfully written
-     *         to the socket channel, or, {@code false} if not.
-     */
-    private int writeChannel(ByteBuffer buffer) throws IOException {
-        return channel.write(buffer);
-      }
 
-    /**
-     * {@inheritDoc}
-     */
-  @Override
-    public synchronized void close() throws IOException {
-        saslContext.dispose();
-        saslContext=null;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-  @Override
-    public boolean isOpen() {
-        return saslContext != null;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-  @Override
-    public int getAppBufSize() {
-        return saslContext.getBufSize(Sasl.MAX_BUFFER);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-  @Override
-    public Certificate[] getClientCertificateChain() {
-        return new Certificate[0];
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-  @Override
-    public int getSSF() {
-        return saslContext.getSSF();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-  @Override
-    public ByteChannel wrapChannel(ByteChannel channel) {
-        return this;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-  @Override
-    public String getName() {
-        return name;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-  @Override
-    public boolean isSecure() {
-        return true;
-    }
+  /**
+   * Write the specified byte buffer to the socket channel.
+   *
+   * @param buffer
+   *          The byte buffer to write to the socket channel.
+   * @return {@code true} if the byte buffer was successfully written to the
+   *         socket channel, or, {@code false} if not.
+   */
+  private int writeChannel(final ByteBuffer buffer) throws IOException
+  {
+    return channel.write(buffer);
+  }
 
 }
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