/*
 * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.jndi.ldap;

import java.io.UnsupportedEncodingException;

A BER encoder.
Author:Jagane Sundar, Scott Seligman, Vincent Ryan
/** * A BER encoder. * * @author Jagane Sundar * @author Scott Seligman * @author Vincent Ryan */
public final class BerEncoder extends Ber { private int curSeqIndex; private int seqOffset[]; private static final int INITIAL_SEQUENCES = 16; private static final int DEFAULT_BUFSIZE = 1024; // When buf is full, expand its size by the following factor. private static final int BUF_GROWTH_FACTOR = 8;
Creates a BER buffer for encoding.
/** * Creates a BER buffer for encoding. */
public BerEncoder() { this(DEFAULT_BUFSIZE); }
Creates a BER buffer of a specified size for encoding. Specify the initial bufsize. Buffer will be expanded as needed.
Params:
  • bufsize – The number of bytes for the buffer.
/** * Creates a BER buffer of a specified size for encoding. * Specify the initial bufsize. Buffer will be expanded as needed. * @param bufsize The number of bytes for the buffer. */
public BerEncoder(int bufsize) { buf = new byte[bufsize]; this.bufsize = bufsize; offset = 0; seqOffset = new int[INITIAL_SEQUENCES]; curSeqIndex = 0; }
Resets encoder to state when newly constructed. Zeros out internal data structures.
/** * Resets encoder to state when newly constructed. Zeros out * internal data structures. */
public void reset() { while (offset > 0) { buf[--offset] = 0; } while (curSeqIndex > 0) { seqOffset[--curSeqIndex] = 0; } } // ------------------ Accessor methods ------------
Gets the number of encoded bytes in this BER buffer.
/** * Gets the number of encoded bytes in this BER buffer. */
public int getDataLen() { return offset; }
Gets the buffer that contains the BER encoding. Throws an exception if unmatched beginSeq() and endSeq() pairs were encountered. Not entire buffer contains encoded bytes. Use getDataLen() to determine number of encoded bytes. Use getBuffer(true) to get rid of excess bytes in array.
Throws:
  • IllegalStateException – If buffer contains unbalanced sequence.
/** * Gets the buffer that contains the BER encoding. Throws an * exception if unmatched beginSeq() and endSeq() pairs were * encountered. Not entire buffer contains encoded bytes. * Use getDataLen() to determine number of encoded bytes. * Use getBuffer(true) to get rid of excess bytes in array. * @throws IllegalStateException If buffer contains unbalanced sequence. */
public byte[] getBuf() { if (curSeqIndex != 0) { throw new IllegalStateException("BER encode error: Unbalanced SEQUENCEs."); } return buf; // shared buffer, be careful to use this method. }
Gets the buffer that contains the BER encoding, trimming unused bytes.
Throws:
  • IllegalStateException – If buffer contains unbalanced sequence.
/** * Gets the buffer that contains the BER encoding, trimming unused bytes. * * @throws IllegalStateException If buffer contains unbalanced sequence. */
public byte[] getTrimmedBuf() { int len = getDataLen(); byte[] trimBuf = new byte[len]; System.arraycopy(getBuf(), 0, trimBuf, 0, len); return trimBuf; } // -------------- encoding methods -------------
Begin encoding a sequence with a tag.
/** * Begin encoding a sequence with a tag. */
public void beginSeq(int tag) { // Double the size of the SEQUENCE array if it overflows if (curSeqIndex >= seqOffset.length) { int[] seqOffsetTmp = new int[seqOffset.length * 2]; for (int i = 0; i < seqOffset.length; i++) { seqOffsetTmp[i] = seqOffset[i]; } seqOffset = seqOffsetTmp; } encodeByte(tag); seqOffset[curSeqIndex] = offset; // Save space for sequence length. // %%% Currently we save enough space for sequences up to 64k. // For larger sequences we'll need to shift the data to the right // in endSeq(). If we could instead pad the length field with // zeros, it would be a big win. ensureFreeBytes(3); offset += 3; curSeqIndex++; }
Terminate a BER sequence.
/** * 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.
/** * 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.
/** * 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.
BER integer w tag ::= tag berlength byte {byte}*
/** * 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.
BER boolean ::= 0x01 0x01 {0xff|0x00}
/** * 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
BER boolean w TAG ::= tag 0x01 {0xff|0x00}
/** * 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.
BER string ::= 0x04 strlen byte1 byte2...
The string is converted into bytes using UTF-8 or ISO-Latin-1.
/** * 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.
BER string w TAG ::= tag strlen byte1 byte2...
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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; } } }