429 lines · 12.2 KB · Java
RawDownload
/* * Copyright (c) 1999, 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  * @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.     */    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.     * @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.     */    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.     */    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.     */    public byte[] getBuf() {        if (curSeqIndex != 0) {            throw new IllegalStateException("BER encode error: Unbalanced SEQUENCEs.");        }        return buf;    }     /**     * 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.     */    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.      */    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;        }    }}