0N/A/*
0N/A * Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
0N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
0N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
0N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
0N/A * or visit www.oracle.com if you need additional information or have any
0N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.jndi.ldap;
0N/A
0N/Aimport java.io.UnsupportedEncodingException;
0N/A
0N/A/**
0N/A * A BER encoder.
0N/A *
0N/A * @author Jagane Sundar
0N/A * @author Scott Seligman
661N/A * @author Vincent Ryan
0N/A */
0N/Apublic final class BerEncoder extends Ber {
0N/A
0N/A private int curSeqIndex;
0N/A private int seqOffset[];
0N/A private static final int INITIAL_SEQUENCES = 16;
0N/A private static final int DEFAULT_BUFSIZE = 1024;
0N/A
0N/A // When buf is full, expand its size by the following factor.
0N/A private static final int BUF_GROWTH_FACTOR = 8;
0N/A
0N/A /**
0N/A * Creates a BER buffer for encoding.
0N/A */
0N/A public BerEncoder() {
0N/A this(DEFAULT_BUFSIZE);
0N/A }
0N/A
0N/A /**
0N/A * Creates a BER buffer of a specified size for encoding.
0N/A * Specify the initial bufsize. Buffer will be expanded as needed.
0N/A * @param bufsize The number of bytes for the buffer.
0N/A */
0N/A public BerEncoder(int bufsize) {
0N/A buf = new byte[bufsize];
0N/A this.bufsize = bufsize;
0N/A offset = 0;
0N/A
0N/A seqOffset = new int[INITIAL_SEQUENCES];
0N/A curSeqIndex = 0;
0N/A }
0N/A
0N/A /**
0N/A * Resets encoder to state when newly constructed. Zeros out
0N/A * internal data structures.
0N/A */
0N/A public void reset() {
0N/A while (offset > 0) {
0N/A buf[--offset] = 0;
0N/A }
0N/A while (curSeqIndex > 0) {
0N/A seqOffset[--curSeqIndex] = 0;
0N/A }
0N/A }
0N/A
0N/A// ------------------ Accessor methods ------------
0N/A
0N/A /**
0N/A * Gets the number of encoded bytes in this BER buffer.
0N/A */
0N/A public int getDataLen() {
0N/A return offset;
0N/A }
0N/A
0N/A /**
0N/A * Gets the buffer that contains the BER encoding. Throws an
0N/A * exception if unmatched beginSeq() and endSeq() pairs were
0N/A * encountered. Not entire buffer contains encoded bytes.
0N/A * Use getDataLen() to determine number of encoded bytes.
0N/A * Use getBuffer(true) to get rid of excess bytes in array.
0N/A * @throws IllegalStateException If buffer contains unbalanced sequence.
0N/A */
0N/A public byte[] getBuf() {
0N/A if (curSeqIndex != 0) {
0N/A throw new IllegalStateException("BER encode error: Unbalanced SEQUENCEs.");
0N/A }
0N/A return buf;
0N/A }
0N/A
0N/A /**
0N/A * Gets the buffer that contains the BER encoding, trimming unused bytes.
0N/A *
0N/A * @throws IllegalStateException If buffer contains unbalanced sequence.
0N/A */
0N/A public byte[] getTrimmedBuf() {
0N/A int len = getDataLen();
0N/A byte[] trimBuf = new byte[len];
0N/A
0N/A System.arraycopy(getBuf(), 0, trimBuf, 0, len);
0N/A return trimBuf;
0N/A }
0N/A
0N/A// -------------- encoding methods -------------
0N/A
0N/A /**
0N/A * Begin encoding a sequence with a tag.
0N/A */
0N/A public void beginSeq(int tag) {
0N/A
0N/A // Double the size of the SEQUENCE array if it overflows
0N/A if (curSeqIndex >= seqOffset.length) {
0N/A int[] seqOffsetTmp = new int[seqOffset.length * 2];
0N/A
661N/A for (int i = 0; i < seqOffset.length; i++) {
0N/A seqOffsetTmp[i] = seqOffset[i];
0N/A }
0N/A seqOffset = seqOffsetTmp;
0N/A }
0N/A
0N/A encodeByte(tag);
0N/A seqOffset[curSeqIndex] = offset;
0N/A
0N/A // Save space for sequence length.
0N/A // %%% Currently we save enough space for sequences up to 64k.
0N/A // For larger sequences we'll need to shift the data to the right
0N/A // in endSeq(). If we could instead pad the length field with
0N/A // zeros, it would be a big win.
0N/A ensureFreeBytes(3);
0N/A offset += 3;
0N/A
0N/A curSeqIndex++;
0N/A }
/**
* Terminate a BER sequence.
*/
public void endSeq() throws EncodeException {
curSeqIndex--;
if (curSeqIndex < 0) {
throw new IllegalStateException("BER encode error: Unbalanced SEQUENCEs.");
}
int start = seqOffset[curSeqIndex] + 3; // index beyond length field
int len = offset - start;
if (len <= 0x7f) {
shiftSeqData(start, len, -2);
buf[seqOffset[curSeqIndex]] = (byte) len;
} else if (len <= 0xff) {
shiftSeqData(start, len, -1);
buf[seqOffset[curSeqIndex]] = (byte) 0x81;
buf[seqOffset[curSeqIndex] + 1] = (byte) len;
} else if (len <= 0xffff) {
buf[seqOffset[curSeqIndex]] = (byte) 0x82;
buf[seqOffset[curSeqIndex] + 1] = (byte) (len >> 8);
buf[seqOffset[curSeqIndex] + 2] = (byte) len;
} else if (len <= 0xffffff) {
shiftSeqData(start, len, 1);
buf[seqOffset[curSeqIndex]] = (byte) 0x83;
buf[seqOffset[curSeqIndex] + 1] = (byte) (len >> 16);
buf[seqOffset[curSeqIndex] + 2] = (byte) (len >> 8);
buf[seqOffset[curSeqIndex] + 3] = (byte) len;
} else {
throw new EncodeException("SEQUENCE too long");
}
}
/**
* Shifts contents of buf in the range [start,start+len) a specified amount.
* Positive shift value means shift to the right.
*/
private void shiftSeqData(int start, int len, int shift) {
if (shift > 0) {
ensureFreeBytes(shift);
}
System.arraycopy(buf, start, buf, start + shift, len);
offset += shift;
}
/**
* Encode a single byte.
*/
public void encodeByte(int b) {
ensureFreeBytes(1);
buf[offset++] = (byte) b;
}
/*
private void deleteByte() {
offset--;
}
*/
/*
* Encodes an int.
*<blockquote><pre>
* BER integer ::= 0x02 berlength byte {byte}*
*</pre></blockquote>
*/
public void encodeInt(int i) {
encodeInt(i, 0x02);
}
/**
* Encodes an int and a tag.
*<blockquote><pre>
* BER integer w tag ::= tag berlength byte {byte}*
*</pre></blockquote>
*/
public void encodeInt(int i, int tag) {
int mask = 0xff800000;
int intsize = 4;
while( (((i & mask) == 0) || ((i & mask) == mask)) && (intsize > 1) ) {
intsize--;
i <<= 8;
}
encodeInt(i, tag, intsize);
}
//
// encodes an int using numbytes for the actual encoding.
//
private void encodeInt(int i, int tag, int intsize) {
//
// integer ::= 0x02 asnlength byte {byte}*
//
if (intsize > 4) {
throw new IllegalArgumentException("BER encode error: INTEGER too long.");
}
ensureFreeBytes(2 + intsize);
buf[offset++] = (byte) tag;
buf[offset++] = (byte) intsize;
int mask = 0xff000000;
while (intsize-- > 0) {
buf[offset++] = (byte) ((i & mask) >> 24);
i <<= 8;
}
}
/**
* Encodes a boolean.
*<blockquote><pre>
* BER boolean ::= 0x01 0x01 {0xff|0x00}
*</pre></blockquote>
*/
public void encodeBoolean(boolean b) {
encodeBoolean(b, ASN_BOOLEAN);
}
/**
* Encodes a boolean and a tag
*<blockquote><pre>
* BER boolean w TAG ::= tag 0x01 {0xff|0x00}
*</pre></blockquote>
*/
public void encodeBoolean(boolean b, int tag) {
ensureFreeBytes(3);
buf[offset++] = (byte) tag;
buf[offset++] = 0x01;
buf[offset++] = b ? (byte) 0xff : (byte) 0x00;
}
/**
* Encodes a string.
*<blockquote><pre>
* BER string ::= 0x04 strlen byte1 byte2...
*</pre></blockquote>
* The string is converted into bytes using UTF-8 or ISO-Latin-1.
*/
public void encodeString(String str, boolean encodeUTF8)
throws EncodeException {
encodeString(str, ASN_OCTET_STR, encodeUTF8);
}
/**
* Encodes a string and a tag.
*<blockquote><pre>
* BER string w TAG ::= tag strlen byte1 byte2...
*</pre></blockquote>
*/
public void encodeString(String str, int tag, boolean encodeUTF8)
throws EncodeException {
encodeByte(tag);
int i = 0;
int count;
byte[] bytes = null;
if (str == null) {
count = 0;
} else if (encodeUTF8) {
try {
bytes = str.getBytes("UTF8");
count = bytes.length;
} catch (UnsupportedEncodingException e) {
throw new EncodeException("UTF8 not available on platform");
}
} else {
try {
bytes = str.getBytes("8859_1");
count = bytes.length;
} catch (UnsupportedEncodingException e) {
throw new EncodeException("8859_1 not available on platform");
}
}
encodeLength(count);
ensureFreeBytes(count);
while (i < count) {
buf[offset++] = bytes[i++];
}
}
/**
* Encodes a portion of an octet string and a tag.
*/
public void encodeOctetString(byte tb[], int tag, int tboffset, int length)
throws EncodeException {
encodeByte(tag);
encodeLength(length);
if (length > 0) {
ensureFreeBytes(length);
System.arraycopy(tb, tboffset, buf, offset, length);
offset += length;
}
}
/**
* Encodes an octet string and a tag.
*/
public void encodeOctetString(byte tb[], int tag) throws EncodeException {
encodeOctetString(tb, tag, 0, tb.length);
}
private void encodeLength(int len) throws EncodeException {
ensureFreeBytes(4); // worst case
if (len < 128) {
buf[offset++] = (byte) len;
} else if (len <= 0xff) {
buf[offset++] = (byte) 0x81;
buf[offset++] = (byte) len;
} else if (len <= 0xffff) {
buf[offset++] = (byte) 0x82;
buf[offset++] = (byte) (len >> 8);
buf[offset++] = (byte) (len & 0xff);
} else if (len <= 0xffffff) {
buf[offset++] = (byte) 0x83;
buf[offset++] = (byte) (len >> 16);
buf[offset++] = (byte) (len >> 8);
buf[offset++] = (byte) (len & 0xff);
} else {
throw new EncodeException("string too long");
}
}
/**
* Encodes an array of strings.
*/
public void encodeStringArray(String strs[], boolean encodeUTF8)
throws EncodeException {
if (strs == null)
return;
for (int i = 0; i < strs.length; i++) {
encodeString(strs[i], encodeUTF8);
}
}
/*
private void encodeNull() {
//
// NULL ::= 0x05 0x00
//
encodeByte(0x05);
encodeByte(0x00);
}
*/
/**
* Ensures that there are at least "len" unused bytes in "buf".
* When more space is needed "buf" is expanded by a factor of
* BUF_GROWTH_FACTOR, then "len" bytes are added if "buf" still
* isn't large enough.
*/
private void ensureFreeBytes(int len) {
if (bufsize - offset < len) {
int newsize = bufsize * BUF_GROWTH_FACTOR;
if (newsize - offset < len) {
newsize += len;
}
byte newbuf[] = new byte[newsize];
// Only copy bytes in the range [0, offset)
System.arraycopy(buf, 0, newbuf, 0, offset);
buf = newbuf;
bufsize = newsize;
}
}
}