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/api/ClientConnection.java                     |   21 
 opends/src/server/org/opends/server/extensions/ConnectionSecurityProvider.java    |   18 
 opends/src/server/org/opends/server/extensions/SASLContext.java                   |   63 ++
 opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java    |   15 
 opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java |   14 
 opends/src/server/org/opends/server/extensions/RedirectingByteChannel.java        |    2 
 opends/src/server/org/opends/server/extensions/TLSByteChannel.java                |  737 +++++++++++++++-------------
 opends/src/server/org/opends/server/extensions/SASLByteChannel.java               |  579 +++++++++-------------
 opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java      |   47 -
 9 files changed, 712 insertions(+), 784 deletions(-)

diff --git a/opends/src/server/org/opends/server/api/ClientConnection.java b/opends/src/server/org/opends/server/api/ClientConnection.java
index 3fe256f..3ce44cd 100644
--- a/opends/src/server/org/opends/server/api/ClientConnection.java
+++ b/opends/src/server/org/opends/server/api/ClientConnection.java
@@ -23,13 +23,14 @@
  *
  *
  *      Copyright 2006-2009 Sun Microsystems, Inc.
- *      Portions copyright 2011 ForgeRock AS
+ *      Portions copyright 2011-2012 ForgeRock AS
  */
 package org.opends.server.api;
 
 
 
 import java.net.InetAddress;
+import java.nio.channels.ByteChannel;
 import java.nio.channels.Selector;
 import java.nio.channels.SocketChannel;
 import java.util.Collection;
@@ -47,7 +48,6 @@
 import org.opends.server.core.PluginConfigManager;
 import org.opends.server.core.SearchOperation;
 import org.opends.server.core.networkgroups.NetworkGroup;
-import org.opends.server.extensions.RedirectingByteChannel;
 import org.opends.server.loggers.debug.DebugTracer;
 import org.opends.server.types.Attribute;
 import org.opends.server.types.AttributeType;
@@ -1327,7 +1327,7 @@
    *
    * @return The lowest level channel associated with a connection.
    */
-  public RedirectingByteChannel getChannel() {
+  public ByteChannel getChannel() {
     // By default, return null, which indicates that there should
     // be no channel.  Subclasses should override this if
     // they want to support a channel.
@@ -1351,21 +1351,6 @@
 
 
   /**
-   * Return the largest application buffer size that should be used
-   * for a connection.
-   *
-   * @return The application buffer size.
-   */
-  public int getAppBufferSize() {
-    // By default, return 0, which indicates that there should
-    // be no application buffer size.  Subclasses should override
-    //this if they want to support a application buffer size.
-    return 0;
-  }
-
-
-
-  /**
    * Retrieves the size limit that will be enforced for searches
    * performed using this client connection.
    *
diff --git a/opends/src/server/org/opends/server/extensions/ConnectionSecurityProvider.java b/opends/src/server/org/opends/server/extensions/ConnectionSecurityProvider.java
index dbe19d4..287ea8b 100644
--- a/opends/src/server/org/opends/server/extensions/ConnectionSecurityProvider.java
+++ b/opends/src/server/org/opends/server/extensions/ConnectionSecurityProvider.java
@@ -42,15 +42,6 @@
 {
 
   /**
-   * Return a buffer size of the byte channel.
-   *
-   * @return Integer representing the byte channel application buffer size.
-   */
-  int getAppBufSize();
-
-
-
-  /**
    * Return a certificate chain array.
    *
    * @return A certificate chain array.
@@ -87,12 +78,9 @@
 
 
   /**
-   * Factory method: creates a new security ByteChannel layer wrapping the
-   * provided ByteChannel.
+   * Returns the security provider's byte channel.
    *
-   * @param channel
-   *          The byte channel to be wrapped.
-   * @return A byte channel wrapping the specified byte channel.
+   * @return The security provider's byte channel.
    */
-  ByteChannel wrapChannel(ByteChannel channel);
+  ByteChannel getChannel();
 }
diff --git a/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java b/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
index 2c9a519..e459de7 100644
--- a/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
+++ b/opends/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandler.java
@@ -23,6 +23,7 @@
  *
  *
  *      Copyright 2006-2009 Sun Microsystems, Inc.
+ *      Portions copyright 2012 ForgeRock AS.
  */
 package org.opends.server.extensions;
 
@@ -163,18 +164,7 @@
          (SASLContext) clientConn.getSASLAuthStateInfo();
       if(saslContext == null) {
           try {
-            //If the connection is secure already (i.e., TLS), then make the
-            //receive buffers sizes match.
-            if(clientConn.isSecure()) {
-              HashMap<String, String>secProps =
-                                      new HashMap<String,String>(saslProps);
-              int maxBuf = clientConn.getAppBufferSize();
-              secProps.put(Sasl.MAX_BUFFER, Integer.toString(maxBuf));
-              saslContext = SASLContext.createSASLContext(secProps,
-                                      serverFQDN, SASL_MECHANISM_DIGEST_MD5,
-                                      identityMapper);
-            } else
-              saslContext = SASLContext.createSASLContext(saslProps, serverFQDN,
+            saslContext = SASLContext.createSASLContext(saslProps, serverFQDN,
                             SASL_MECHANISM_DIGEST_MD5, identityMapper);
           } catch (SaslException ex) {
               if (debugEnabled()) {
diff --git a/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java b/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java
index 2c30316..213714d 100644
--- a/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java
+++ b/opends/src/server/org/opends/server/extensions/GSSAPISASLMechanismHandler.java
@@ -23,7 +23,7 @@
  *
  *
  *      Copyright 2006-2009 Sun Microsystems, Inc.
- *      Portions Copyright 2011 ForgeRock AS
+ *      Portions Copyright 2011-2012 ForgeRock AS
  */
 package org.opends.server.extensions;
 
@@ -398,19 +398,8 @@
     SASLContext saslContext = (SASLContext) clientConn.getSASLAuthStateInfo();
     if (saslContext == null) {
       try {
-        //If the connection is secure already (i.e., TLS), then make the
-        //receive buffers sizes match.
-        if(clientConn.isSecure()) {
-          HashMap<String, String>secProps =
-                                  new HashMap<String,String>(saslProps);
-          int maxBuf = clientConn.getAppBufferSize();
-          secProps.put(Sasl.MAX_BUFFER, Integer.toString(maxBuf));
-          saslContext = SASLContext.createSASLContext(secProps, serverFQDN,
+        saslContext = SASLContext.createSASLContext(saslProps, serverFQDN,
                                   SASL_MECHANISM_GSSAPI, identityMapper);
-        } else {
-          saslContext = SASLContext.createSASLContext(saslProps, serverFQDN,
-                                  SASL_MECHANISM_GSSAPI, identityMapper);
-        }
       } catch (SaslException ex) {
         if (debugEnabled())
           TRACER.debugCaught(DebugLogLevel.ERROR, ex);
diff --git a/opends/src/server/org/opends/server/extensions/RedirectingByteChannel.java b/opends/src/server/org/opends/server/extensions/RedirectingByteChannel.java
index 134fdce..61fd118 100644
--- a/opends/src/server/org/opends/server/extensions/RedirectingByteChannel.java
+++ b/opends/src/server/org/opends/server/extensions/RedirectingByteChannel.java
@@ -143,7 +143,7 @@
    */
   public final void redirect(final ConnectionSecurityProvider provider)
   {
-    redirect = provider.wrapChannel(child);
+    redirect = provider.getChannel();
   }
 
 
diff --git a/opends/src/server/org/opends/server/extensions/SASLByteChannel.java b/opends/src/server/org/opends/server/extensions/SASLByteChannel.java
index 03ab350..bf6ece9 100644
--- a/opends/src/server/org/opends/server/extensions/SASLByteChannel.java
+++ b/opends/src/server/org/opends/server/extensions/SASLByteChannel.java
@@ -35,9 +35,6 @@
 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;
 
 
@@ -46,10 +43,229 @@
  * This class implements a SASL byte channel that can be used during
  * confidentiality and integrity.
  */
-public class SASLByteChannel implements ByteChannel, ConnectionSecurityProvider
+public final class SASLByteChannel implements ConnectionSecurityProvider
 {
 
   /**
+   * Private implementation.
+   */
+  private final class ByteChannelImpl implements ByteChannel
+  {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void close() throws IOException
+    {
+      synchronized (readLock)
+      {
+        synchronized (writeLock)
+        {
+          saslContext.dispose();
+          channel.close();
+        }
+      }
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isOpen()
+    {
+      return saslContext != null;
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int read(final ByteBuffer unwrappedData) throws IOException
+    {
+      synchronized (readLock)
+      {
+        // Only read and unwrap new data if needed.
+        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 byte 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}
+     */
+    @Override
+    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)
+      {
+        // Write data in sendBufferSize segments.
+        while (unwrappedData.hasRemaining())
+        {
+          final int remaining = unwrappedData.remaining();
+          final int wrapSize = (remaining < sendUnwrappedBufferSize) ? remaining
+              : sendUnwrappedBufferSize;
+
+          final byte[] wrappedDataBytes;
+          if (unwrappedData.hasArray())
+          {
+            // Avoid extra copy if ByteBuffer is array based.
+            wrappedDataBytes = saslContext.wrap(unwrappedData.array(),
+                unwrappedData.arrayOffset(), wrapSize);
+            unwrappedData.position(unwrappedData.position() + wrapSize);
+          }
+          else
+          {
+            // Non-array based ByteBuffer, so copy.
+            unwrappedData.get(sendUnwrappedBytes, 0, wrapSize);
+            wrappedDataBytes = saslContext
+                .wrap(sendUnwrappedBytes, 0, wrapSize);
+            unwrappedData.position(unwrappedData.position() + wrapSize);
+          }
+
+          // Encode SASL packet: 4 byte length + wrapped data.
+          if (sendWrappedBuffer.capacity() < wrappedDataBytes.length + 4)
+          {
+            // Resize the send buffer.
+            sendWrappedBuffer = ByteBuffer
+                .allocate(wrappedDataBytes.length + 4);
+          }
+          sendWrappedBuffer.clear();
+          sendWrappedBuffer.putInt(wrappedDataBytes.length);
+          sendWrappedBuffer.put(wrappedDataBytes);
+          sendWrappedBuffer.flip();
+
+          // Write the SASL packet: our IO stack will block until all the data
+          // is written.
+          channel.write(sendWrappedBuffer);
+        }
+      }
+
+      return bytesWritten;
+    }
+
+
+
+    // Attempt to read and unwrap the next SASL packet.
+    private int doRecvAndUnwrap() throws IOException
+    {
+      // Read the encoded packet length first.
+      if (recvWrappedLength < 0)
+      {
+        final int read = channel.read(recvWrappedLengthBuffer);
+        if (read <= 0)
+        {
+          // No data read or end of stream.
+          return read;
+        }
+
+        if (recvWrappedLengthBuffer.hasRemaining())
+        {
+          // Unable to read the length, so no data available yet.
+          return 0;
+        }
+
+        // Decode the length and reset the length buffer.
+        recvWrappedLengthBuffer.flip();
+        recvWrappedLength = recvWrappedLengthBuffer.getInt();
+        recvWrappedLengthBuffer.clear();
+
+        // Check that the length is valid.
+        if (recvWrappedLength > recvWrappedBufferMaximumSize)
+        {
+          throw new IOException(
+              "Client sent a SASL packet specifying a length "
+                  + recvWrappedLength
+                  + " which exceeds the negotiated limit of "
+                  + recvWrappedBufferMaximumSize);
+        }
+
+        if (recvWrappedLength < 0)
+        {
+          throw new IOException(
+              "Client sent a SASL packet specifying a negative length "
+                  + recvWrappedLength);
+        }
+
+        // Prepare the recv buffer for reading.
+        recvWrappedBuffer.clear();
+        recvWrappedBuffer.limit(recvWrappedLength);
+      }
+
+      // Read wrapped data.
+      final int read = channel.read(recvWrappedBuffer);
+      if (read <= 0)
+      {
+        // No data read or end of stream.
+        return read;
+      }
+
+      if (recvWrappedBuffer.hasRemaining())
+      {
+        // Unable to read the full packet, so no data available yet.
+        return 0;
+      }
+
+      // The complete packet has been read, so unwrap it.
+      recvWrappedBuffer.flip();
+      final byte[] unwrappedDataBytes = saslContext.unwrap(
+          recvWrappedBuffer.array(), 0, recvWrappedLength);
+      recvWrappedLength = -1;
+
+      if (recvUnwrappedBuffer.capacity() < unwrappedDataBytes.length)
+      {
+        // Resize the recv buffer (this shouldn't ever happen).
+        recvUnwrappedBuffer = ByteBuffer.allocate(unwrappedDataBytes.length);
+      }
+
+      recvUnwrappedBuffer.clear();
+      recvUnwrappedBuffer.put(unwrappedDataBytes);
+      recvUnwrappedBuffer.flip();
+
+      return recvUnwrappedBuffer.remaining();
+    }
+
+  }
+
+
+
+  /**
    * Return a SASL byte channel instance created using the specified parameters.
    *
    * @param c
@@ -68,31 +284,23 @@
 
 
 
-  // The SASL context associated with the provider
-  private SASLContext saslContext;
-
-  // The byte channel associated with this provider.
-  private final RedirectingByteChannel channel;
-
-  // The number of bytes in the length buffer.
-  private static final int lengthSize = 4;
-
-  // Length of the buffer.
-  private int bufLength;
-
-  // The SASL mechanism name.
   private final String name;
+  private final ByteChannel channel;
+  private final ByteChannelImpl pimpl = new ByteChannelImpl();
+  private final SASLContext saslContext;
 
-  // Buffers used in reading and decoding (unwrap)
-  private final ByteBuffer readBuffer, decodeBuffer;
+  private ByteBuffer recvUnwrappedBuffer;
+  private final ByteBuffer recvWrappedBuffer;
+  private final int recvWrappedBufferMaximumSize;
+  private int recvWrappedLength = -1;
+  private final ByteBuffer recvWrappedLengthBuffer = ByteBuffer.allocate(4);
 
-  // How many bytes of the subsequent buffer is needed to complete a partially
-  // read buffer.
-  private int neededBytes = 0;
+  private final int sendUnwrappedBufferSize;
+  private final byte[] sendUnwrappedBytes;
+  private ByteBuffer sendWrappedBuffer;
 
-  // Used to not reset the buffer length size because the first 4 bytes of a
-  // buffer are not size bytes.
-  private boolean reading = false;
+  private final Object readLock = new Object();
+  private final Object writeLock = new Object();
 
 
 
@@ -112,10 +320,16 @@
   {
     this.name = name;
     this.saslContext = saslContext;
-    this.channel = connection.getChannel();
-    this.readBuffer = ByteBuffer.allocate(connection.getAppBufferSize());
-    this.decodeBuffer = ByteBuffer.allocate(connection.getAppBufferSize()
-        + lengthSize);
+
+    channel = connection.getChannel();
+    recvWrappedBufferMaximumSize = saslContext.getMaxReceiveBufferSize();
+    sendUnwrappedBufferSize = saslContext.getMaxRawSendBufferSize();
+
+    recvWrappedBuffer = ByteBuffer.allocate(recvWrappedBufferMaximumSize);
+    recvUnwrappedBuffer = ByteBuffer.allocate(recvWrappedBufferMaximumSize);
+    recvUnwrappedBuffer.flip(); // Initially nothing has been received.
+    sendUnwrappedBytes = new byte[sendUnwrappedBufferSize];
+    sendWrappedBuffer = ByteBuffer.allocate(sendUnwrappedBufferSize + 64);
   }
 
 
@@ -124,21 +338,9 @@
    * {@inheritDoc}
    */
   @Override
-  public synchronized void close() throws IOException
+  public ByteChannel getChannel()
   {
-    saslContext.dispose();
-    saslContext = null;
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  public int getAppBufSize()
-  {
-    return saslContext.getBufSize(Sasl.MAX_BUFFER);
+    return pimpl;
   }
 
 
@@ -180,300 +382,9 @@
    * {@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;
-    }
-
-    final 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);
-    }
-
-    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 0;
-    }
-    else
-    {
-      readBuffer.flip();
-      decodeBuffer.put(readBuffer);
-      final byte[] inBytes = decodeBuffer.array();
-      final byte[] clearBytes = saslContext.unwrap(inBytes, lengthSize,
-          bufLength);
-      decodeBuffer.clear();
-      clearDst.put(clearBytes);
-      readBuffer.clear();
-      return clearDst.position();
-    }
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  @Override
-  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);
-      }
-      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;
-      }
-      total -= count;
-    }
-    if (total > 0)
-    {
-      return -1;
-    }
-    else
-    {
-      return byteBuf.position();
-    }
-  }
-
-
-
-  /**
-   * 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(final ByteBuffer buffer) throws IOException
-  {
-    return channel.write(buffer);
-  }
-
 }
diff --git a/opends/src/server/org/opends/server/extensions/SASLContext.java b/opends/src/server/org/opends/server/extensions/SASLContext.java
index 78a6c5b..a549f70 100644
--- a/opends/src/server/org/opends/server/extensions/SASLContext.java
+++ b/opends/src/server/org/opends/server/extensions/SASLContext.java
@@ -447,16 +447,65 @@
 
 
   /**
-   * Return the negotiated buffer size.
+   * Returns the negotiated maximum size of protected data which can be received
+   * from the client.
    *
-   * @param prop
-   *          The buffer size property to return.
-   * @return The value of the negotiated buffer size.
+   * @return The negotiated maximum size of protected data which can be received
+   *         from the client.
    */
-  int getBufSize(final String prop)
+  int getMaxReceiveBufferSize()
   {
-    final String sizeStr = (String) saslServer.getNegotiatedProperty(prop);
-    return Integer.parseInt(sizeStr);
+    String str = (String) saslServer.getNegotiatedProperty(Sasl.MAX_BUFFER);
+    if (str != null)
+    {
+      try
+      {
+        return Integer.parseInt(str);
+      }
+      catch (NumberFormatException e)
+      {
+        if (debugEnabled())
+        {
+          TRACER.debugCaught(DebugLogLevel.ERROR, e);
+        }
+      }
+    }
+
+    // Default buffer size if not specified according to Java SASL
+    // documentation.
+    return 65536;
+  }
+
+
+
+  /**
+   * Returns the negotiated maximum size of raw data which can be sent to the
+   * client.
+   *
+   * @return The negotiated maximum size of raw data which can be sent to the
+   *         client.
+   */
+  int getMaxRawSendBufferSize()
+  {
+    String str = (String) saslServer.getNegotiatedProperty(Sasl.RAW_SEND_SIZE);
+    if (str != null)
+    {
+      try
+      {
+        return Integer.parseInt(str);
+      }
+      catch (NumberFormatException e)
+      {
+        if (debugEnabled())
+        {
+          TRACER.debugCaught(DebugLogLevel.ERROR, e);
+        }
+      }
+    }
+
+    // Default buffer size if not specified according to Java SASL
+    // documentation.
+    return 65536;
   }
 
 
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;
-  }
 }
diff --git a/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java b/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
index b67cc3a..3af5e14 100644
--- a/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
+++ b/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
@@ -23,7 +23,7 @@
  *
  *
  *      Copyright 2006-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2010-2011 ForgeRock AS.
+ *      Portions Copyright 2010-2012 ForgeRock AS.
  */
 package org.opends.server.protocols.ldap;
 
@@ -360,7 +360,7 @@
 
   private ASN1ByteChannelReader asn1Reader;
 
-  private int APPLICATION_BUFFER_SIZE = 4096;
+  private final int bufferSize;
 
   private final RedirectingByteChannel saslChannel;
   private final RedirectingByteChannel tlsChannel;
@@ -420,7 +420,7 @@
       this.useNanoTime=DirectoryServer.getUseNanoTime();
     }
 
-    APPLICATION_BUFFER_SIZE = connectionHandler.getBufferSize();
+    bufferSize = connectionHandler.getBufferSize();
 
     tlsChannel =
         RedirectingByteChannel.getRedirectingByteChannel(
@@ -428,7 +428,7 @@
     saslChannel =
         RedirectingByteChannel.getRedirectingByteChannel(tlsChannel);
     this.asn1Reader =
-        ASN1.getReader(saslChannel, APPLICATION_BUFFER_SIZE, connectionHandler
+        ASN1.getReader(saslChannel, bufferSize, connectionHandler
             .getMaxRequestSize());
     writeLock = new ReentrantLock();
 
@@ -939,16 +939,8 @@
     {
       if (asn1Writer == null)
       {
-        if (isSecure())
-        {
-          int appBufSize = activeProvider.getAppBufSize();
-          asn1Writer = ASN1.getWriter(saslChannel, writeLock, appBufSize);
-        }
-        else
-        {
-          asn1Writer = ASN1.getWriter(saslChannel, writeLock,
-                  APPLICATION_BUFFER_SIZE);
-        }
+        asn1Writer = ASN1.getWriter(saslChannel, writeLock,
+                  bufferSize);
         asn1WriterMap.put(currentThread, asn1Writer);
       }
 
@@ -2590,9 +2582,6 @@
    */
   public void enableTLS()
   {
-    this.asn1Reader =
-        ASN1.getReader(saslChannel, tlsPendingProvider.getAppBufSize(),
-            connectionHandler.getMaxRequestSize());
     activeProvider = tlsPendingProvider;
     tlsChannel.redirect(tlsPendingProvider);
     tlsPendingProvider = null;
@@ -2608,9 +2597,6 @@
    */
   public void enableSSL(ConnectionSecurityProvider sslProvider)
   {
-    this.asn1Reader =
-        ASN1.getReader(saslChannel, sslProvider.getAppBufSize(),
-            connectionHandler.getMaxRequestSize());
     activeProvider = sslProvider;
     tlsChannel.redirect(sslProvider);
   }
@@ -2624,10 +2610,6 @@
   {
     activeProvider = saslPendingProvider;
     saslChannel.redirect(saslPendingProvider);
-    asn1Reader =
-        ASN1.getReader(saslChannel,
-            saslPendingProvider.getAppBufSize(), connectionHandler
-                .getMaxRequestSize());
     saslPendingProvider = null;
   }
 
@@ -2657,7 +2639,7 @@
    * @return The TLS redirecting byte channel.
    */
    @Override
-   public RedirectingByteChannel getChannel() {
+   public ByteChannel getChannel() {
      return this.tlsChannel;
    }
 
@@ -2678,21 +2660,6 @@
 
 
   /**
-   * Retrieves the application buffer size used in a LDAP client connection.
-   * If a active security provider is being used, then the application buffer
-   * size of that provider is returned.
-   *
-   * @return The application buffer size.
-   */
-  @Override
-  public int getAppBufferSize() {
-    if(activeProvider != null)
-      return activeProvider.getAppBufSize();
-    else
-      return APPLICATION_BUFFER_SIZE;
-  }
-
-  /**
    * {@inheritDoc}
    */
   @Override

--
Gitblit v1.10.0