mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

matthew_swift
28.47.2010 f2160f4bd1c8ac67e5a86a6710d431e8932877f9
sdk/src/com/sun/opends/sdk/ldap/ASN1BufferReader.java
File was renamed from sdk/src/com/sun/opends/sdk/ldap/ASN1StreamReader.java
@@ -22,17 +22,20 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2010 Sun Microsystems, Inc.
 */
package com.sun.opends.sdk.ldap;
import static com.sun.opends.sdk.ldap.LDAPConstants.ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES;
import static com.sun.opends.sdk.ldap.LDAPConstants.ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE;
import static com.sun.opends.sdk.ldap.LDAPConstants.ELEMENT_READ_STATE_NEED_TYPE;
import static com.sun.opends.sdk.ldap.LDAPConstants.ELEMENT_READ_STATE_NEED_VALUE_BYTES;
import static com.sun.opends.sdk.messages.Messages.*;
import static com.sun.opends.sdk.ldap.LDAPConstants.*;
import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.util.logging.Level;
import org.opends.sdk.ByteString;
@@ -42,8 +45,9 @@
import org.opends.sdk.asn1.ASN1Reader;
import org.opends.sdk.asn1.AbstractASN1Reader;
import com.sun.grizzly.streams.StreamReader;
import com.sun.grizzly.utils.PoolableObject;
import com.sun.grizzly.Buffer;
import com.sun.grizzly.memory.ByteBuffersBuffer;
import com.sun.grizzly.memory.CompositeBuffer;
import com.sun.opends.sdk.util.StaticUtils;
@@ -51,10 +55,9 @@
/**
 * Grizzly ASN1 reader implementation.
 */
public final class ASN1StreamReader extends AbstractASN1Reader implements
    PoolableObject, ASN1Reader
final class ASN1BufferReader extends AbstractASN1Reader implements ASN1Reader
{
  class ChildSequenceLimiter implements SequenceLimiter
  private final class ChildSequenceLimiter implements SequenceLimiter
  {
    private SequenceLimiter parent;
@@ -66,12 +69,12 @@
    public void checkLimit(int readSize) throws IOException,
        BufferUnderflowException
    public void checkLimit(final int readSize) throws IOException
    {
      if ((readLimit > 0) && (bytesRead + readSize > readLimit))
      {
        throw new BufferUnderflowException();
        final LocalizableMessage message = ERR_ASN1_TRUNCATED_LENGTH_BYTE.get();
        throw DecodeException.fatalError(message);
      }
      parent.checkLimit(readSize);
@@ -85,17 +88,17 @@
    {
      parent.checkLimit(remaining());
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINE)
          && remaining() > 0)
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINE) && remaining() > 0)
      {
        StaticUtils.DEBUG_LOG.fine(String.format(
            "Ignoring %d unused trailing bytes in ASN.1 SEQUENCE",
            remaining()));
        StaticUtils.DEBUG_LOG
            .fine(String.format(
                "Ignoring %d unused trailing bytes in ASN.1 SEQUENCE",
                remaining()));
      }
      for (int i = 0; i < remaining(); i++)
      {
        streamReader.readByte();
        buffer.get();
      }
      return parent;
@@ -110,7 +113,7 @@
    public ChildSequenceLimiter startSequence(int readLimit)
    public ChildSequenceLimiter startSequence(final int readLimit)
    {
      if (child == null)
      {
@@ -127,21 +130,27 @@
  class RootSequenceLimiter implements SequenceLimiter
  private final class RootSequenceLimiter implements SequenceLimiter
  {
    private ChildSequenceLimiter child;
    public void checkLimit(int readSize)
    public void checkLimit(final int readSize) throws IOException
    {
      if (buffer.remaining() < readSize)
      {
        final LocalizableMessage message = ERR_ASN1_TRUNCATED_LENGTH_BYTE.get();
        throw DecodeException.fatalError(message);
      }
    }
    public ChildSequenceLimiter endSequence() throws DecodeException
    {
      LocalizableMessage message = ERR_ASN1_SEQUENCE_READ_NOT_STARTED.get();
      final LocalizableMessage message = ERR_ASN1_SEQUENCE_READ_NOT_STARTED
          .get();
      throw new IllegalStateException(message.toString());
    }
@@ -149,12 +158,12 @@
    public int remaining()
    {
      return streamReader.availableDataSize();
      return buffer.remaining();
    }
    public ChildSequenceLimiter startSequence(int readLimit)
    public ChildSequenceLimiter startSequence(final int readLimit)
    {
      if (child == null)
      {
@@ -173,8 +182,7 @@
  private interface SequenceLimiter
  {
    public void checkLimit(int readSize) throws IOException,
        BufferUnderflowException;
    public void checkLimit(int readSize) throws IOException;
@@ -203,29 +211,28 @@
  private final int maxElementSize;
  private StreamReader streamReader;
  private final RootSequenceLimiter rootLimiter;
  private final CompositeBuffer buffer;
  private SequenceLimiter readLimiter;
  private final byte[] buffer;
  private final byte[] stringBuffer;
  /**
   * Creates a new ASN1 reader whose source is the provided input stream
   * and having a user defined maximum BER element size.
   * Creates a new ASN1 reader whose source is the provided input stream and
   * having a user defined maximum BER element size.
   *
   * @param maxElementSize
   *          The maximum BER element size, or <code>0</code> to
   *          indicate that there is no limit.
   *          The maximum BER element size, or <code>0</code> to indicate that
   *          there is no limit.
   */
  public ASN1StreamReader(int maxElementSize)
  ASN1BufferReader(final int maxElementSize)
  {
    this.readLimiter = this.rootLimiter = new RootSequenceLimiter();
    this.buffer = new byte[MAX_STRING_BUFFER_SIZE];
    this.readLimiter = new RootSequenceLimiter();
    this.stringBuffer = new byte[MAX_STRING_BUFFER_SIZE];
    this.maxElementSize = maxElementSize;
    this.buffer = ByteBuffersBuffer.create();
  }
@@ -238,55 +245,45 @@
   */
  public void close() throws IOException
  {
    // close the stream reader.
    streamReader.close();
    buffer.dispose();
  }
  /**
   * Determines if a complete ASN.1 element is ready to be read from the
   * stream reader.
   * Determines if a complete ASN.1 element is ready to be read from the stream
   * reader.
   *
   * @return <code>true</code> if another complete element is available
   *         or <code>false</code> otherwise.
   * @return <code>true</code> if another complete element is available or
   *         <code>false</code> otherwise.
   * @throws IOException
   *           If an error occurs while trying to decode an ASN1
   *           element.
   *           If an error occurs while trying to decode an ASN1 element.
   */
  public boolean elementAvailable() throws IOException
  {
    if ((state == ELEMENT_READ_STATE_NEED_TYPE) && !needTypeState(true))
    {
      return false;
    }
    if ((state == ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE)
        && !needFirstLengthByteState(true))
    {
      return false;
    }
    return !((state == ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES)
        && !needAdditionalLengthBytesState(true)) &&
        peekLength <= readLimiter.remaining();
    return !((state == ELEMENT_READ_STATE_NEED_TYPE) && !needTypeState(true))
        && !((state == ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE)
            && !needFirstLengthByteState(true))
        && !((state == ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES)
            && !needAdditionalLengthBytesState(true))
        && peekLength <= readLimiter.remaining();
  }
  /**
   * Determines if the input stream contains at least one ASN.1 element
   * to be read.
   * Determines if the input stream contains at least one ASN.1 element to be
   * read.
   *
   * @return <code>true</code> if another element is available or
   *         <code>false</code> otherwise.
   * @throws IOException
   *           If an error occurs while trying to decode an ASN1
   *           element.
   *           If an error occurs while trying to decode an ASN1 element.
   */
  public boolean hasNextElement() throws IOException
  {
    return (state != ELEMENT_READ_STATE_NEED_TYPE)
        || needTypeState(true);
    return (state != ELEMENT_READ_STATE_NEED_TYPE) || needTypeState(true);
  }
@@ -328,13 +325,6 @@
  public void prepare()
  {
    // Nothing to do
  }
  /**
   * {@inheritDoc}
   */
@@ -345,18 +335,19 @@
    if (peekLength != 1)
    {
      LocalizableMessage message = ERR_ASN1_BOOLEAN_INVALID_LENGTH.get(peekLength);
      final LocalizableMessage message = ERR_ASN1_BOOLEAN_INVALID_LENGTH
          .get(peekLength);
      throw DecodeException.fatalError(message);
    }
    readLimiter.checkLimit(peekLength);
    byte readByte = streamReader.readByte();
    final byte readByte = buffer.get();
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG.finest(String.format(
          "READ ASN.1 BOOLEAN(type=0x%x, length=%d, value=%s)",
          peekType, peekLength, String.valueOf(readByte != 0x00)));
          "READ ASN.1 BOOLEAN(type=0x%x, length=%d, value=%s)", peekType,
          peekLength, String.valueOf(readByte != 0x00)));
    }
    state = ELEMENT_READ_STATE_NEED_TYPE;
@@ -368,15 +359,13 @@
  /**
   * {@inheritDoc}
   */
  public void readEndSequence() throws IOException,
      IllegalStateException
  public void readEndSequence() throws IOException, IllegalStateException
  {
    readLimiter = readLimiter.endSequence();
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG.finest(String
          .format("READ ASN.1 END SEQUENCE"));
      StaticUtils.DEBUG_LOG.finest(String.format("READ ASN.1 END SEQUENCE"));
    }
    // Reset the state
@@ -407,7 +396,8 @@
    if ((peekLength < 1) || (peekLength > 4))
    {
      LocalizableMessage message = ERR_ASN1_INTEGER_INVALID_LENGTH.get(peekLength);
      final LocalizableMessage message = ERR_ASN1_INTEGER_INVALID_LENGTH
          .get(peekLength);
      throw DecodeException.fatalError(message);
    }
@@ -428,7 +418,8 @@
    if ((peekLength < 1) || (peekLength > 8))
    {
      LocalizableMessage message = ERR_ASN1_INTEGER_INVALID_LENGTH.get(peekLength);
      final LocalizableMessage message = ERR_ASN1_INTEGER_INVALID_LENGTH
          .get(peekLength);
      throw DecodeException.fatalError(message);
    }
@@ -438,7 +429,7 @@
      long longValue = 0;
      for (int i = 0; i < peekLength; i++)
      {
        int readByte = streamReader.readByte();
        final int readByte = buffer.get();
        if ((i == 0) && (((byte) readByte) < 0))
        {
          longValue = 0xFFFFFFFFFFFFFFFFL;
@@ -454,7 +445,7 @@
      int intValue = 0;
      for (int i = 0; i < peekLength; i++)
      {
        int readByte = streamReader.readByte();
        final int readByte = buffer.get();
        if ((i == 0) && (((byte) readByte) < 0))
        {
          intValue = 0xFFFFFFFF;
@@ -465,8 +456,8 @@
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
      {
        StaticUtils.DEBUG_LOG.finest(String.format(
            "READ ASN.1 INTEGER(type=0x%x, length=%d, value=%d)",
            peekType, peekLength, intValue));
            "READ ASN.1 INTEGER(type=0x%x, length=%d, value=%d)", peekType,
            peekLength, intValue));
      }
      state = ELEMENT_READ_STATE_NEED_TYPE;
@@ -487,15 +478,15 @@
    // Make sure that the decoded length is exactly zero byte.
    if (peekLength != 0)
    {
      LocalizableMessage message = ERR_ASN1_NULL_INVALID_LENGTH.get(peekLength);
      final LocalizableMessage message = ERR_ASN1_NULL_INVALID_LENGTH
          .get(peekLength);
      throw DecodeException.fatalError(message);
    }
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG.finest(String
          .format("READ ASN.1 NULL(type=0x%x, length=%d)", peekType,
              peekLength));
      StaticUtils.DEBUG_LOG.finest(String.format(
          "READ ASN.1 NULL(type=0x%x, length=%d)", peekType, peekLength));
    }
    state = ELEMENT_READ_STATE_NEED_TYPE;
@@ -519,14 +510,14 @@
    readLimiter.checkLimit(peekLength);
    // Copy the value and construct the element to return.
    byte[] value = new byte[peekLength];
    streamReader.readByteArray(value);
    final byte[] value = new byte[peekLength];
    buffer.get(value);
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG.finest(String.format(
          "READ ASN.1 OCTETSTRING(type=0x%x, length=%d)", peekType,
          peekLength));
      StaticUtils.DEBUG_LOG
          .finest(String.format("READ ASN.1 OCTETSTRING(type=0x%x, length=%d)",
              peekType, peekLength));
    }
    state = ELEMENT_READ_STATE_NEED_TYPE;
@@ -538,7 +529,7 @@
  /**
   * {@inheritDoc}
   */
  public ByteStringBuilder readOctetString(ByteStringBuilder builder)
  public ByteStringBuilder readOctetString(final ByteStringBuilder builder)
      throws IOException
  {
    // Read the header if haven't done so already
@@ -555,14 +546,14 @@
    // TODO: Is there a more efficient way to do this?
    for (int i = 0; i < peekLength; i++)
    {
      builder.append(streamReader.readByte());
      builder.append(buffer.get());
    }
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG.finest(String.format(
          "READ ASN.1 OCTETSTRING(type=0x%x, length=%d)", peekType,
          peekLength));
      StaticUtils.DEBUG_LOG
          .finest(String.format("READ ASN.1 OCTETSTRING(type=0x%x, length=%d)",
              peekType, peekLength));
    }
    state = ELEMENT_READ_STATE_NEED_TYPE;
@@ -586,9 +577,9 @@
    }
    byte[] readBuffer;
    if (peekLength <= buffer.length)
    if (peekLength <= stringBuffer.length)
    {
      readBuffer = buffer;
      readBuffer = stringBuffer;
    }
    else
    {
@@ -596,7 +587,7 @@
    }
    readLimiter.checkLimit(peekLength);
    streamReader.readByteArray(readBuffer, 0, peekLength);
    buffer.get(readBuffer, 0, peekLength);
    state = ELEMENT_READ_STATE_NEED_TYPE;
@@ -605,7 +596,7 @@
    {
      str = new String(readBuffer, 0, peekLength, "UTF-8");
    }
    catch (Exception e)
    catch (final Exception e)
    {
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.WARNING))
      {
@@ -614,14 +605,14 @@
                + e.toString());
      }
      str = new String(buffer, 0, peekLength);
      str = new String(stringBuffer, 0, peekLength);
    }
    if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
    {
      StaticUtils.DEBUG_LOG.finest(String.format(
          "READ ASN.1 OCTETSTRING(type=0x%x, length=%d, value=%s)",
          peekType, peekLength, str));
          "READ ASN.1 OCTETSTRING(type=0x%x, length=%d, value=%s)", peekType,
          peekLength, str));
    }
    return str;
@@ -664,24 +655,6 @@
  public void release()
  {
    streamReader = null;
    peekLength = -1;
    peekType = 0;
    readLimiter = rootLimiter;
    state = ELEMENT_READ_STATE_NEED_TYPE;
  }
  public void setStreamReader(StreamReader streamReader)
  {
    this.streamReader = streamReader;
  }
  /**
   * {@inheritDoc}
   */
@@ -693,7 +666,7 @@
    readLimiter.checkLimit(peekLength);
    for (int i = 0; i < peekLength; i++)
    {
      streamReader.readByte();
      buffer.get();
    }
    state = ELEMENT_READ_STATE_NEED_TYPE;
    return this;
@@ -701,18 +674,31 @@
  void appendBytesRead(final Buffer buffer)
  {
    this.buffer.append(buffer);
  }
  void disposeBytesRead()
  {
    this.buffer.disposeUnused();
  }
  /**
   * Internal helper method reading the additional ASN.1 length bytes
   * and transition to the next state if successful.
   * Internal helper method reading the additional ASN.1 length bytes and
   * transition to the next state if successful.
   *
   * @param ensureRead
   *          <code>true</code> to check for availability first.
   * @return <code>true</code> if the length bytes was successfully
   *         read.
   * @return <code>true</code> if the length bytes was successfully read.
   * @throws IOException
   *           If an error occurs while reading from the stream.
   */
  private boolean needAdditionalLengthBytesState(boolean ensureRead)
  private boolean needAdditionalLengthBytesState(final boolean ensureRead)
      throws IOException
  {
    if (ensureRead && (readLimiter.remaining() < lengthBytesNeeded))
@@ -724,7 +710,7 @@
    readLimiter.checkLimit(lengthBytesNeeded);
    while (lengthBytesNeeded > 0)
    {
      readByte = streamReader.readByte();
      readByte = buffer.get();
      peekLength = (peekLength << 8) | (readByte & 0xFF);
      lengthBytesNeeded--;
    }
@@ -733,8 +719,8 @@
    // message size.
    if ((maxElementSize > 0) && (peekLength > maxElementSize))
    {
      LocalizableMessage m = ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED.get(
          peekLength, maxElementSize);
      final LocalizableMessage m = ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED
          .get(peekLength, maxElementSize);
      throw DecodeException.fatalError(m);
    }
    state = ELEMENT_READ_STATE_NEED_VALUE_BYTES;
@@ -744,17 +730,16 @@
  /**
   * Internal helper method reading the first length bytes and
   * transition to the next state if successful.
   * Internal helper method reading the first length bytes and transition to the
   * next state if successful.
   *
   * @param ensureRead
   *          <code>true</code> to check for availability first.
   * @return <code>true</code> if the length bytes was successfully read
   * @throws IOException
   *           If an error occurs while trying to decode an ASN1
   *           element.
   *           If an error occurs while trying to decode an ASN1 element.
   */
  private boolean needFirstLengthByteState(boolean ensureRead)
  private boolean needFirstLengthByteState(final boolean ensureRead)
      throws IOException
  {
    if (ensureRead && (readLimiter.remaining() <= 0))
@@ -763,14 +748,14 @@
    }
    readLimiter.checkLimit(1);
    byte readByte = streamReader.readByte();
    byte readByte = buffer.get();
    peekLength = (readByte & 0x7F);
    if (peekLength != readByte)
    {
      lengthBytesNeeded = peekLength;
      if (lengthBytesNeeded > 4)
      {
        LocalizableMessage message = ERR_ASN1_INVALID_NUM_LENGTH_BYTES
        final LocalizableMessage message = ERR_ASN1_INVALID_NUM_LENGTH_BYTES
            .get(lengthBytesNeeded);
        throw DecodeException.fatalError(message);
      }
@@ -785,7 +770,7 @@
      readLimiter.checkLimit(lengthBytesNeeded);
      while (lengthBytesNeeded > 0)
      {
        readByte = streamReader.readByte();
        readByte = buffer.get();
        peekLength = (peekLength << 8) | (readByte & 0xFF);
        lengthBytesNeeded--;
      }
@@ -795,8 +780,8 @@
    // message size.
    if ((maxElementSize > 0) && (peekLength > maxElementSize))
    {
      LocalizableMessage m = ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED.get(
          peekLength, maxElementSize);
      final LocalizableMessage m = ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED
          .get(peekLength, maxElementSize);
      throw DecodeException.fatalError(m);
    }
    state = ELEMENT_READ_STATE_NEED_VALUE_BYTES;
@@ -806,17 +791,16 @@
  /**
   * Internal helper method reading the ASN.1 type byte and transition
   * to the next state if successful.
   * Internal helper method reading the ASN.1 type byte and transition to the
   * next state if successful.
   *
   * @param ensureRead
   *          <code>true</code> to check for availability first.
   * @return <code>true</code> if the type byte was successfully read
   * @throws IOException
   *           If an error occurs while trying to decode an ASN1
   *           element.
   *           If an error occurs while trying to decode an ASN1 element.
   */
  private boolean needTypeState(boolean ensureRead) throws IOException
  private boolean needTypeState(final boolean ensureRead) throws IOException
  {
    // Read just the type.
    if (ensureRead && (readLimiter.remaining() <= 0))
@@ -825,7 +809,7 @@
    }
    readLimiter.checkLimit(1);
    peekType = streamReader.readByte();
    peekType = buffer.get();
    state = ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE;
    return true;
  }