From da98067ef4d2bc52762e8f6586c876b0f8b941ae Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Thu, 16 Feb 2012 15:48:56 +0000
Subject: [PATCH] Additional fix for OPENDJ-420: Rare SSLExceptions while handling LDAPS connections and big LDAP searches

---
 opends/src/server/org/opends/server/extensions/SASLByteChannel.java |  148 +++++++++++++++++++++++++------------------------
 1 files changed, 75 insertions(+), 73 deletions(-)

diff --git a/opends/src/server/org/opends/server/extensions/SASLByteChannel.java b/opends/src/server/org/opends/server/extensions/SASLByteChannel.java
index bf6ece9..d24ac6e 100644
--- a/opends/src/server/org/opends/server/extensions/SASLByteChannel.java
+++ b/opends/src/server/org/opends/server/extensions/SASLByteChannel.java
@@ -163,8 +163,8 @@
           if (sendWrappedBuffer.capacity() < wrappedDataBytes.length + 4)
           {
             // Resize the send buffer.
-            sendWrappedBuffer = ByteBuffer
-                .allocate(wrappedDataBytes.length + 4);
+            sendWrappedBuffer =
+                ByteBuffer.allocate(wrappedDataBytes.length + 4);
           }
           sendWrappedBuffer.clear();
           sendWrappedBuffer.putInt(wrappedDataBytes.length);
@@ -185,82 +185,85 @@
     // Attempt to read and unwrap the next SASL packet.
     private int doRecvAndUnwrap() throws IOException
     {
-      // Read the encoded packet length first.
-      if (recvWrappedLength < 0)
+      // Read SASL packets until some unwrapped data is produced or no more
+      // data is available on the underlying channel.
+      while (true)
       {
-        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);
-        }
-
+        // Read the wrapped packet length first.
         if (recvWrappedLength < 0)
         {
-          throw new IOException(
-              "Client sent a SASL packet specifying a negative length "
-                  + recvWrappedLength);
+          // The channel read may only partially fill the buffer due to
+          // buffering in the underlying channel layer (e.g. SSL layer), so
+          // repeatedly read until the length has been read or we are sure
+          // that we are unable to proceed.
+          while (recvWrappedLengthBuffer.hasRemaining())
+          {
+            final int read = channel.read(recvWrappedLengthBuffer);
+            if (read <= 0)
+            {
+              // Not enough data available or end of stream.
+              return read;
+            }
+          }
+
+          // 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);
         }
 
-        // Prepare the recv buffer for reading.
-        recvWrappedBuffer.clear();
-        recvWrappedBuffer.limit(recvWrappedLength);
+        // Read the wrapped packet data.
+
+        // The channel read may only partially fill the buffer due to
+        // buffering in the underlying channel layer (e.g. SSL layer), so
+        // repeatedly read until the data has been read or we are sure
+        // that we are unable to proceed.
+        while (recvWrappedBuffer.hasRemaining())
+        {
+          final int read = channel.read(recvWrappedBuffer);
+          if (read <= 0)
+          {
+            // Not enough data available or end of stream.
+            return read;
+          }
+        }
+
+        // The complete packet has been read, so unwrap it.
+        recvWrappedBuffer.flip();
+        final byte[] unwrappedDataBytes = saslContext.unwrap(
+            recvWrappedBuffer.array(), 0, recvWrappedLength);
+        recvWrappedLength = -1;
+
+        // Only return the unwrapped data if it was non-empty, otherwise try to
+        // read another SASL packet.
+        if (unwrappedDataBytes.length > 0)
+        {
+          recvUnwrappedBuffer = ByteBuffer.wrap(unwrappedDataBytes);
+          return recvUnwrappedBuffer.remaining();
+        }
       }
-
-      // 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();
     }
-
   }
 
 
@@ -326,8 +329,7 @@
     sendUnwrappedBufferSize = saslContext.getMaxRawSendBufferSize();
 
     recvWrappedBuffer = ByteBuffer.allocate(recvWrappedBufferMaximumSize);
-    recvUnwrappedBuffer = ByteBuffer.allocate(recvWrappedBufferMaximumSize);
-    recvUnwrappedBuffer.flip(); // Initially nothing has been received.
+    recvUnwrappedBuffer = ByteBuffer.allocate(0);
     sendUnwrappedBytes = new byte[sendUnwrappedBufferSize];
     sendWrappedBuffer = ByteBuffer.allocate(sendUnwrappedBufferSize + 64);
   }

--
Gitblit v1.10.0