/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2008 Sun Microsystems, Inc. */ package org.opends.sdk.asn1; import static com.sun.opends.sdk.messages.Messages.*; import static org.opends.sdk.asn1.ASN1Constants.*; import java.io.IOException; import java.util.LinkedList; import java.util.logging.Level; import org.opends.sdk.*; import com.sun.opends.sdk.util.StaticUtils; /** * An ASN.1 reader that reads from a {@link ByteSequenceReader}. */ final class ASN1ByteSequenceReader extends AbstractASN1Reader implements ASN1Reader { private int state = ELEMENT_READ_STATE_NEED_TYPE; private byte peekType = 0; private int peekLength = -1; private final int maxElementSize; private ByteSequenceReader reader; private final LinkedList readerStack; /** * Creates a new ASN1 reader whose source is the provided byte * sequence reader and having a user defined maximum BER element size. * * @param reader * The byte sequence reader to be read. * @param maxElementSize * The maximum BER element size, or 0 to * indicate that there is no limit. */ ASN1ByteSequenceReader(ByteSequenceReader reader, int maxElementSize) { this.reader = reader; this.readerStack = new LinkedList(); this.maxElementSize = maxElementSize; } /** * {@inheritDoc} */ public void close() throws IOException { readerStack.clear(); } /** * {@inheritDoc} */ public boolean elementAvailable() throws IOException { if ((state == ELEMENT_READ_STATE_NEED_TYPE) && !needTypeState(false)) { return false; } if ((state == ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE) && !needFirstLengthByteState(false)) { return false; } return peekLength <= reader.remaining(); } /** * {@inheritDoc} */ public boolean hasNextElement() throws IOException { return (state != ELEMENT_READ_STATE_NEED_TYPE) || needTypeState(false); } /** * {@inheritDoc} */ public int peekLength() throws IOException { peekType(); if (state == ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE) { needFirstLengthByteState(true); } return peekLength; } /** * {@inheritDoc} */ public byte peekType() throws IOException { if (state == ELEMENT_READ_STATE_NEED_TYPE) { // Read just the type. if (reader.remaining() <= 0) { LocalizableMessage message = ERR_ASN1_TRUCATED_TYPE_BYTE.get(); throw DecodeException.fatalError(message); } int type = reader.get(); peekType = (byte) type; state = ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE; } return peekType; } /** * {@inheritDoc} */ public boolean readBoolean() throws IOException { // Read the header if haven't done so already peekLength(); if (peekLength != 1) { LocalizableMessage message = ERR_ASN1_BOOLEAN_INVALID_LENGTH.get(peekLength); throw DecodeException.fatalError(message); } if (reader.remaining() < peekLength) { LocalizableMessage message = ERR_ASN1_BOOLEAN_TRUNCATED_VALUE .get(peekLength); throw DecodeException.fatalError(message); } int readByte = reader.get(); state = ELEMENT_READ_STATE_NEED_TYPE; return readByte != 0x00; } /** * {@inheritDoc} */ public void readEndSequence() throws IOException, IllegalStateException { if (readerStack.isEmpty()) { LocalizableMessage message = ERR_ASN1_SEQUENCE_READ_NOT_STARTED.get(); throw new IllegalStateException(message.toString()); } if ((reader.remaining() > 0) && StaticUtils.DEBUG_LOG.isLoggable(Level.FINE)) { StaticUtils.DEBUG_LOG.fine("Ignoring " + reader.remaining() + " unused trailing bytes in " + "ASN.1 SEQUENCE"); } reader = readerStack.removeFirst(); // Reset the state state = ELEMENT_READ_STATE_NEED_TYPE; } /** * {@inheritDoc} */ public void readEndSet() throws IOException { // From an implementation point of view, a set is equivalent to a // sequence. readEndSequence(); } /** * {@inheritDoc} */ public int readEnumerated() throws IOException { // Read the header if haven't done so already peekLength(); if ((peekLength < 1) || (peekLength > 4)) { LocalizableMessage message = ERR_ASN1_INTEGER_INVALID_LENGTH.get(peekLength); throw DecodeException.fatalError(message); } // From an implementation point of view, an enumerated value is // equivalent to an integer. return (int) readInteger(); } /** * {@inheritDoc} */ public long readInteger() throws IOException { // Read the header if haven't done so already peekLength(); if ((peekLength < 1) || (peekLength > 8)) { LocalizableMessage message = ERR_ASN1_INTEGER_INVALID_LENGTH.get(peekLength); throw DecodeException.fatalError(message); } if (reader.remaining() < peekLength) { LocalizableMessage message = ERR_ASN1_INTEGER_TRUNCATED_VALUE .get(peekLength); throw DecodeException.fatalError(message); } if (peekLength > 4) { long longValue = 0; for (int i = 0; i < peekLength; i++) { int readByte = reader.get(); if ((i == 0) && (readByte < 0)) { longValue = 0xFFFFFFFFFFFFFFFFL; } longValue = (longValue << 8) | (readByte & 0xFF); } state = ELEMENT_READ_STATE_NEED_TYPE; return longValue; } else { int intValue = 0; for (int i = 0; i < peekLength; i++) { int readByte = reader.get(); if ((i == 0) && (readByte < 0)) { intValue = 0xFFFFFFFF; } intValue = (intValue << 8) | (readByte & 0xFF); } state = ELEMENT_READ_STATE_NEED_TYPE; return intValue; } } /** * {@inheritDoc} */ public void readNull() throws IOException { // Read the header if haven't done so already peekLength(); // Make sure that the decoded length is exactly zero byte. if (peekLength != 0) { LocalizableMessage message = ERR_ASN1_NULL_INVALID_LENGTH.get(peekLength); throw DecodeException.fatalError(message); } state = ELEMENT_READ_STATE_NEED_TYPE; } /** * {@inheritDoc} */ public ByteString readOctetString() throws IOException { // Read the header if haven't done so already peekLength(); if (reader.remaining() < peekLength) { LocalizableMessage message = ERR_ASN1_OCTET_STRING_TRUNCATED_VALUE .get(peekLength); throw DecodeException.fatalError(message); } state = ELEMENT_READ_STATE_NEED_TYPE; return reader.getByteString(peekLength); } /** * {@inheritDoc} */ public ByteStringBuilder readOctetString(ByteStringBuilder builder) throws IOException { // Read the header if haven't done so already peekLength(); // Copy the value. if (reader.remaining() < peekLength) { LocalizableMessage message = ERR_ASN1_OCTET_STRING_TRUNCATED_VALUE .get(peekLength); throw DecodeException.fatalError(message); } builder.append(reader, peekLength); state = ELEMENT_READ_STATE_NEED_TYPE; return builder; } /** * {@inheritDoc} */ public String readOctetStringAsString() throws IOException { // Read the header if haven't done so already peekLength(); if (reader.remaining() < peekLength) { LocalizableMessage message = ERR_ASN1_OCTET_STRING_TRUNCATED_VALUE .get(peekLength); throw DecodeException.fatalError(message); } state = ELEMENT_READ_STATE_NEED_TYPE; return reader.getString(peekLength); } /** * {@inheritDoc} */ public void readStartSequence() throws IOException { // Read the header if haven't done so already peekLength(); if (reader.remaining() < peekLength) { LocalizableMessage message = ERR_ASN1_SEQUENCE_SET_TRUNCATED_VALUE .get(peekLength); throw DecodeException.fatalError(message); } ByteSequenceReader subByteString = reader.getByteSequence( peekLength).asReader(); readerStack.addFirst(reader); reader = subByteString; // Reset the state state = ELEMENT_READ_STATE_NEED_TYPE; } /** * {@inheritDoc} */ public void readStartSet() throws IOException { // From an implementation point of view, a set is equivalent to a // sequence. readStartSequence(); } /** * {@inheritDoc} */ public ASN1Reader skipElement() throws IOException { // Read the header if haven't done so already peekLength(); if (reader.remaining() < peekLength) { LocalizableMessage message = ERR_ASN1_SKIP_TRUNCATED_VALUE.get(peekLength); throw DecodeException.fatalError(message); } state = ELEMENT_READ_STATE_NEED_TYPE; reader.skip(peekLength); return this; } /** * Internal helper method reading the first length bytes and * transition to the next state if successful. * * @param throwEofException * true to throw an exception when the end of * the sequence is encountered. * @return true if the length bytes was successfully read * @throws IOException * If an error occurs while trying to decode an ASN1 * element. */ private boolean needFirstLengthByteState(boolean throwEofException) throws IOException { if (reader.remaining() <= 0) { if (throwEofException) { LocalizableMessage message = ERR_ASN1_TRUNCATED_LENGTH_BYTE.get(); throw DecodeException.fatalError(message); } return false; } int readByte = reader.get(); peekLength = (readByte & 0x7F); if (peekLength != readByte) { int lengthBytesNeeded = peekLength; if (lengthBytesNeeded > 4) { LocalizableMessage message = ERR_ASN1_INVALID_NUM_LENGTH_BYTES .get(lengthBytesNeeded); throw DecodeException.fatalError(message); } peekLength = 0x00; if (reader.remaining() < lengthBytesNeeded) { if (throwEofException) { LocalizableMessage message = ERR_ASN1_TRUNCATED_LENGTH_BYTES .get(lengthBytesNeeded); throw DecodeException.fatalError(message); } return false; } while (lengthBytesNeeded > 0) { readByte = reader.get(); peekLength = (peekLength << 8) | (readByte & 0xFF); lengthBytesNeeded--; } } // Make sure that the element is not larger than the maximum allowed // message size. if ((maxElementSize > 0) && (peekLength > maxElementSize)) { LocalizableMessage message = ERR_LDAP_CLIENT_DECODE_MAX_REQUEST_SIZE_EXCEEDED .get(peekLength, maxElementSize); throw DecodeException.fatalError(message); } state = ELEMENT_READ_STATE_NEED_VALUE_BYTES; return true; } /** * Internal helper method reading the ASN.1 type byte and transition * to the next state if successful. * * @param throwEofException * true to throw an exception when the end of * the sequence is encountered. * @return true if the type byte was successfully read * @throws IOException * If an error occurs while trying to decode an ASN1 * element. */ private boolean needTypeState(boolean throwEofException) throws IOException { // Read just the type. if (reader.remaining() <= 0) { if (throwEofException) { LocalizableMessage message = ERR_ASN1_TRUCATED_TYPE_BYTE.get(); throw DecodeException.fatalError(message); } return false; } int type = reader.get(); peekType = (byte) type; state = ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE; return true; } }