| | |
| | | package com.forgerock.opendj.grizzly; |
| | | |
| | | import static com.forgerock.opendj.ldap.CoreMessages.*; |
| | | import static com.forgerock.opendj.ldap.LDAPConstants.*; |
| | | import static com.forgerock.opendj.util.StaticUtils.*; |
| | | |
| | | import java.io.IOException; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.opendj.io.ASN1; |
| | | import org.forgerock.opendj.io.ASN1Reader; |
| | | import org.forgerock.opendj.io.AbstractASN1Reader; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | |
| | | final class ASN1BufferReader extends AbstractASN1Reader { |
| | | private final class ChildSequenceLimiter implements SequenceLimiter { |
| | | private SequenceLimiter parent; |
| | | |
| | | private ChildSequenceLimiter child; |
| | | |
| | | private int readLimit; |
| | | |
| | | private int bytesRead; |
| | | |
| | | public void checkLimit(final int readSize) throws IOException { |
| | |
| | | final LocalizableMessage message = ERR_ASN1_TRUNCATED_LENGTH_BYTE.get(); |
| | | throw DecodeException.fatalError(message); |
| | | } |
| | | |
| | | parent.checkLimit(readSize); |
| | | |
| | | bytesRead += readSize; |
| | | } |
| | | |
| | | public SequenceLimiter endSequence() throws IOException { |
| | | parent.checkLimit(remaining()); |
| | | |
| | | if (remaining() > 0) { |
| | | IO_LOG.debug("Ignoring {} unused trailing bytes in ASN.1 SEQUENCE", remaining()); |
| | | } |
| | | |
| | | for (int i = 0; i < remaining(); i++) { |
| | | buffer.get(); |
| | | } |
| | | |
| | | return parent; |
| | | } |
| | | |
| | |
| | | child = new ChildSequenceLimiter(); |
| | | child.parent = this; |
| | | } |
| | | |
| | | child.readLimit = readLimit; |
| | | child.bytesRead = 0; |
| | | |
| | | return child; |
| | | } |
| | | } |
| | |
| | | child = new ChildSequenceLimiter(); |
| | | child.parent = this; |
| | | } |
| | | |
| | | child.readLimit = readLimit; |
| | | child.bytesRead = 0; |
| | | |
| | | return child; |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | private static final int MAX_STRING_BUFFER_SIZE = 1024; |
| | | |
| | | private int state = ELEMENT_READ_STATE_NEED_TYPE; |
| | | |
| | | private int state = ASN1.ELEMENT_READ_STATE_NEED_TYPE; |
| | | private byte peekType = 0; |
| | | |
| | | private int peekLength = -1; |
| | | |
| | | private int lengthBytesNeeded = 0; |
| | | |
| | | private final int maxElementSize; |
| | | |
| | | private final CompositeBuffer buffer; |
| | | |
| | | private SequenceLimiter readLimiter; |
| | | |
| | | private final byte[] stringBuffer; |
| | | |
| | | /** |
| | |
| | | * If an error occurs while trying to decode an ASN1 element. |
| | | */ |
| | | public boolean elementAvailable() throws IOException { |
| | | return !((state == ELEMENT_READ_STATE_NEED_TYPE) && !needTypeState(true)) |
| | | && !((state == ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE) |
| | | return !((state == ASN1.ELEMENT_READ_STATE_NEED_TYPE) |
| | | && !needTypeState(true)) |
| | | && !((state == ASN1.ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE) |
| | | && !needFirstLengthByteState(true)) |
| | | && !((state == ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES) |
| | | && !((state == ASN1.ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES) |
| | | && !needAdditionalLengthBytesState(true)) |
| | | && peekLength <= readLimiter.remaining(); |
| | | |
| | |
| | | * 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 != ASN1.ELEMENT_READ_STATE_NEED_TYPE) || needTypeState(true); |
| | | } |
| | | |
| | | /** |
| | |
| | | peekType(); |
| | | |
| | | switch (state) { |
| | | case ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE: |
| | | case ASN1.ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE: |
| | | needFirstLengthByteState(false); |
| | | break; |
| | | |
| | | case ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES: |
| | | case ASN1.ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES: |
| | | needAdditionalLengthBytesState(false); |
| | | } |
| | | |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | public byte peekType() throws IOException { |
| | | if (state == ELEMENT_READ_STATE_NEED_TYPE) { |
| | | if (state == ASN1.ELEMENT_READ_STATE_NEED_TYPE) { |
| | | needTypeState(false); |
| | | } |
| | | |
| | |
| | | readLimiter.checkLimit(peekLength); |
| | | final byte readByte = buffer.get(); |
| | | |
| | | IO_LOG.trace("READ ASN.1 BOOLEAN(type=0x{}, length={}, value={})", |
| | | byteToHex(peekType), peekLength, String.valueOf(readByte != 0x00)); |
| | | IO_LOG.trace("READ ASN.1 BOOLEAN(type=0x{}, length={}, value={})", byteToHex(peekType), |
| | | peekLength, String.valueOf(readByte != 0x00)); |
| | | |
| | | state = ELEMENT_READ_STATE_NEED_TYPE; |
| | | state = ASN1.ELEMENT_READ_STATE_NEED_TYPE; |
| | | return readByte != 0x00; |
| | | } |
| | | |
| | |
| | | IO_LOG.debug("READ ASN.1 END SEQUENCE"); |
| | | |
| | | // Reset the state |
| | | state = ELEMENT_READ_STATE_NEED_TYPE; |
| | | state = ASN1.ELEMENT_READ_STATE_NEED_TYPE; |
| | | } |
| | | |
| | | /** |
| | |
| | | longValue = (longValue << 8) | (readByte & 0xFF); |
| | | } |
| | | |
| | | state = ELEMENT_READ_STATE_NEED_TYPE; |
| | | state = ASN1.ELEMENT_READ_STATE_NEED_TYPE; |
| | | return longValue; |
| | | } else { |
| | | int intValue = 0; |
| | |
| | | intValue = (intValue << 8) | (readByte & 0xFF); |
| | | } |
| | | |
| | | IO_LOG.trace("READ ASN.1 INTEGER(type=0x{}, length={}, value={})", |
| | | byteToHex(peekType), peekLength, intValue); |
| | | IO_LOG.trace("READ ASN.1 INTEGER(type=0x{}, length={}, value={})", byteToHex(peekType), |
| | | peekLength, intValue); |
| | | |
| | | state = ELEMENT_READ_STATE_NEED_TYPE; |
| | | state = ASN1.ELEMENT_READ_STATE_NEED_TYPE; |
| | | return intValue; |
| | | } |
| | | } |
| | |
| | | throw DecodeException.fatalError(message); |
| | | } |
| | | |
| | | IO_LOG.trace("READ ASN.1 NULL(type=0x{}, length={})", |
| | | byteToHex(peekType), peekLength); |
| | | IO_LOG.trace("READ ASN.1 NULL(type=0x{}, length={})", byteToHex(peekType), peekLength); |
| | | |
| | | state = ELEMENT_READ_STATE_NEED_TYPE; |
| | | state = ASN1.ELEMENT_READ_STATE_NEED_TYPE; |
| | | } |
| | | |
| | | /** |
| | |
| | | peekLength(); |
| | | |
| | | if (peekLength == 0) { |
| | | state = ELEMENT_READ_STATE_NEED_TYPE; |
| | | state = ASN1.ELEMENT_READ_STATE_NEED_TYPE; |
| | | return ByteString.empty(); |
| | | } |
| | | |
| | |
| | | final byte[] value = new byte[peekLength]; |
| | | buffer.get(value); |
| | | |
| | | IO_LOG.trace("READ ASN.1 OCTETSTRING(type=0x{}, length={})", byteToHex(peekType), peekLength); |
| | | IO_LOG.trace("READ ASN.1 OCTETSTRING(type=0x{}, length={})", byteToHex(peekType), |
| | | peekLength); |
| | | |
| | | state = ELEMENT_READ_STATE_NEED_TYPE; |
| | | state = ASN1.ELEMENT_READ_STATE_NEED_TYPE; |
| | | return ByteString.wrap(value); |
| | | } |
| | | |
| | |
| | | peekLength(); |
| | | |
| | | if (peekLength == 0) { |
| | | state = ELEMENT_READ_STATE_NEED_TYPE; |
| | | state = ASN1.ELEMENT_READ_STATE_NEED_TYPE; |
| | | return builder; |
| | | } |
| | | |
| | |
| | | builder.append(buffer.get()); |
| | | } |
| | | |
| | | IO_LOG.trace("READ ASN.1 OCTETSTRING(type=0x{}, length={})", byteToHex(peekType), peekLength); |
| | | IO_LOG.trace("READ ASN.1 OCTETSTRING(type=0x{}, length={})", byteToHex(peekType), |
| | | peekLength); |
| | | |
| | | state = ELEMENT_READ_STATE_NEED_TYPE; |
| | | state = ASN1.ELEMENT_READ_STATE_NEED_TYPE; |
| | | return builder; |
| | | } |
| | | |
| | |
| | | peekLength(); |
| | | |
| | | if (peekLength == 0) { |
| | | state = ELEMENT_READ_STATE_NEED_TYPE; |
| | | state = ASN1.ELEMENT_READ_STATE_NEED_TYPE; |
| | | return ""; |
| | | } |
| | | |
| | |
| | | readLimiter.checkLimit(peekLength); |
| | | buffer.get(readBuffer, 0, peekLength); |
| | | |
| | | state = ELEMENT_READ_STATE_NEED_TYPE; |
| | | state = ASN1.ELEMENT_READ_STATE_NEED_TYPE; |
| | | |
| | | String str; |
| | | try { |
| | |
| | | str = new String(stringBuffer, 0, peekLength); |
| | | } |
| | | |
| | | IO_LOG.trace("READ ASN.1 OCTETSTRING(type=0x{}, length={}, value={})", |
| | | byteToHex(peekType), peekLength, str); |
| | | IO_LOG.trace("READ ASN.1 OCTETSTRING(type=0x{}, length={}, value={})", byteToHex(peekType), |
| | | peekLength, str); |
| | | |
| | | return str; |
| | | } |
| | |
| | | |
| | | readLimiter = readLimiter.startSequence(peekLength); |
| | | |
| | | IO_LOG.trace("READ ASN.1 START SEQUENCE(type=0x{}, length={})", |
| | | byteToHex(peekType), peekLength); |
| | | IO_LOG.trace("READ ASN.1 START SEQUENCE(type=0x{}, length={})", byteToHex(peekType), |
| | | peekLength); |
| | | |
| | | // Reset the state |
| | | state = ELEMENT_READ_STATE_NEED_TYPE; |
| | | state = ASN1.ELEMENT_READ_STATE_NEED_TYPE; |
| | | } |
| | | |
| | | /** |
| | |
| | | for (int i = 0; i < peekLength; i++) { |
| | | buffer.get(); |
| | | } |
| | | state = ELEMENT_READ_STATE_NEED_TYPE; |
| | | state = ASN1.ELEMENT_READ_STATE_NEED_TYPE; |
| | | return this; |
| | | } |
| | | |
| | |
| | | .get(peekLength, maxElementSize); |
| | | throw DecodeException.fatalError(m); |
| | | } |
| | | state = ELEMENT_READ_STATE_NEED_VALUE_BYTES; |
| | | state = ASN1.ELEMENT_READ_STATE_NEED_VALUE_BYTES; |
| | | return true; |
| | | } |
| | | |
| | |
| | | peekLength = 0x00; |
| | | |
| | | if (ensureRead && (readLimiter.remaining() < lengthBytesNeeded)) { |
| | | state = ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES; |
| | | state = ASN1.ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES; |
| | | return false; |
| | | } |
| | | |
| | |
| | | .get(peekLength, maxElementSize); |
| | | throw DecodeException.fatalError(m); |
| | | } |
| | | state = ELEMENT_READ_STATE_NEED_VALUE_BYTES; |
| | | state = ASN1.ELEMENT_READ_STATE_NEED_VALUE_BYTES; |
| | | return true; |
| | | } |
| | | |
| | |
| | | |
| | | readLimiter.checkLimit(1); |
| | | peekType = buffer.get(); |
| | | state = ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE; |
| | | state = ASN1.ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE; |
| | | return true; |
| | | } |
| | | } |