1 /*
   2  * Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.jndi.ldap;
  27 
  28 import java.io.UnsupportedEncodingException;
  29 
  30 /**
  31   * A BER encoder.
  32   *
  33   * @author Jagane Sundar
  34   * @author Scott Seligman
  35   * @author Vincent Ryan
  36   */
  37 public final class BerEncoder extends Ber {
  38 
  39     private int curSeqIndex;
  40     private int seqOffset[];
  41     private static final int INITIAL_SEQUENCES = 16;
  42     private static final int DEFAULT_BUFSIZE = 1024;
  43 
  44     // When buf is full, expand its size by the following factor.
  45     private static final int BUF_GROWTH_FACTOR = 8;
  46 
  47     /**
  48      * Creates a BER buffer for encoding.
  49      */
  50     public BerEncoder() {
  51         this(DEFAULT_BUFSIZE);
  52     }
  53 
  54     /**
  55      * Creates a BER buffer of a specified size for encoding.
  56      * Specify the initial bufsize.  Buffer will be expanded as needed.
  57      * @param bufsize The number of bytes for the buffer.
  58      */
  59     public BerEncoder(int bufsize) {
  60         buf = new byte[bufsize];
  61         this.bufsize = bufsize;
  62         offset = 0;
  63 
  64         seqOffset = new int[INITIAL_SEQUENCES];
  65         curSeqIndex = 0;
  66     }
  67 
  68     /**
  69      * Resets encoder to state when newly constructed.  Zeros out
  70      * internal data structures.
  71      */
  72     public void reset() {
  73         while (offset > 0) {
  74             buf[--offset] = 0;
  75         }
  76         while (curSeqIndex > 0) {
  77             seqOffset[--curSeqIndex] = 0;
  78         }
  79     }
  80 
  81 // ------------------ Accessor methods ------------
  82 
  83     /**
  84      * Gets the number of encoded bytes in this BER buffer.
  85      */
  86     public int getDataLen() {
  87         return offset;
  88     }
  89 
  90     /**
  91      * Gets the buffer that contains the BER encoding. Throws an
  92      * exception if unmatched beginSeq() and endSeq() pairs were
  93      * encountered. Not entire buffer contains encoded bytes.
  94      * Use getDataLen() to determine number of encoded bytes.
  95      * Use getBuffer(true) to get rid of excess bytes in array.
  96      * @throws IllegalStateException If buffer contains unbalanced sequence.
  97      */
  98     public byte[] getBuf() {
  99         if (curSeqIndex != 0) {
 100             throw new IllegalStateException("BER encode error: Unbalanced SEQUENCEs.");
 101         }
 102         return buf;     // shared buffer, be careful to use this method.
 103     }
 104 
 105     /**
 106      * Gets the buffer that contains the BER encoding, trimming unused bytes.
 107      *
 108      * @throws IllegalStateException If buffer contains unbalanced sequence.
 109      */
 110     public byte[] getTrimmedBuf() {
 111         int len = getDataLen();
 112         byte[] trimBuf = new byte[len];
 113 
 114         System.arraycopy(getBuf(), 0, trimBuf, 0, len);
 115         return trimBuf;
 116     }
 117 
 118 // -------------- encoding methods -------------
 119 
 120     /**
 121      * Begin encoding a sequence with a tag.
 122      */
 123     public void beginSeq(int tag) {
 124 
 125         // Double the size of the SEQUENCE array if it overflows
 126         if (curSeqIndex >= seqOffset.length) {
 127             int[] seqOffsetTmp = new int[seqOffset.length * 2];
 128 
 129             for (int i = 0; i < seqOffset.length; i++) {
 130                 seqOffsetTmp[i] = seqOffset[i];
 131             }
 132             seqOffset = seqOffsetTmp;
 133         }
 134 
 135         encodeByte(tag);
 136         seqOffset[curSeqIndex] = offset;
 137 
 138         // Save space for sequence length.
 139         // %%% Currently we save enough space for sequences up to 64k.
 140         //     For larger sequences we'll need to shift the data to the right
 141         //     in endSeq().  If we could instead pad the length field with
 142         //     zeros, it would be a big win.
 143         ensureFreeBytes(3);
 144         offset += 3;
 145 
 146         curSeqIndex++;
 147     }
 148 
 149     /**
 150       * Terminate a BER sequence.
 151       */
 152     public void endSeq() throws EncodeException {
 153         curSeqIndex--;
 154         if (curSeqIndex < 0) {
 155             throw new IllegalStateException("BER encode error: Unbalanced SEQUENCEs.");
 156         }
 157 
 158         int start = seqOffset[curSeqIndex] + 3; // index beyond length field
 159         int len = offset - start;
 160 
 161         if (len <= 0x7f) {
 162             shiftSeqData(start, len, -2);
 163             buf[seqOffset[curSeqIndex]] = (byte) len;
 164         } else if (len <= 0xff) {
 165             shiftSeqData(start, len, -1);
 166             buf[seqOffset[curSeqIndex]] = (byte) 0x81;
 167             buf[seqOffset[curSeqIndex] + 1] = (byte) len;
 168         } else if (len <= 0xffff) {
 169             buf[seqOffset[curSeqIndex]] = (byte) 0x82;
 170             buf[seqOffset[curSeqIndex] + 1] = (byte) (len >> 8);
 171             buf[seqOffset[curSeqIndex] + 2] = (byte) len;
 172         } else if (len <= 0xffffff) {
 173             shiftSeqData(start, len, 1);
 174             buf[seqOffset[curSeqIndex]] = (byte) 0x83;
 175             buf[seqOffset[curSeqIndex] + 1] = (byte) (len >> 16);
 176             buf[seqOffset[curSeqIndex] + 2] = (byte) (len >> 8);
 177             buf[seqOffset[curSeqIndex] + 3] = (byte) len;
 178         } else {
 179             throw new EncodeException("SEQUENCE too long");
 180         }
 181     }
 182 
 183     /**
 184      * Shifts contents of buf in the range [start,start+len) a specified amount.
 185      * Positive shift value means shift to the right.
 186      */
 187     private void shiftSeqData(int start, int len, int shift) {
 188         if (shift > 0) {
 189             ensureFreeBytes(shift);
 190         }
 191         System.arraycopy(buf, start, buf, start + shift, len);
 192         offset += shift;
 193     }
 194 
 195     /**
 196      * Encode a single byte.
 197      */
 198     public void encodeByte(int b) {
 199         ensureFreeBytes(1);
 200         buf[offset++] = (byte) b;
 201     }
 202 
 203 /*
 204     private void deleteByte() {
 205         offset--;
 206     }
 207 */
 208 
 209 
 210     /*
 211      * Encodes an int.
 212      *<blockquote><pre>
 213      * BER integer ::= 0x02 berlength byte {byte}*
 214      *</pre></blockquote>
 215      */
 216     public void encodeInt(int i) {
 217         encodeInt(i, 0x02);
 218     }
 219 
 220     /**
 221      * Encodes an int and a tag.
 222      *<blockquote><pre>
 223      * BER integer w tag ::= tag berlength byte {byte}*
 224      *</pre></blockquote>
 225      */
 226     public void encodeInt(int i, int tag) {
 227         int mask = 0xff800000;
 228         int intsize = 4;
 229 
 230         while( (((i & mask) == 0) || ((i & mask) == mask)) && (intsize > 1) ) {
 231             intsize--;
 232             i <<= 8;
 233         }
 234 
 235         encodeInt(i, tag, intsize);
 236     }
 237 
 238     //
 239     // encodes an int using numbytes for the actual encoding.
 240     //
 241     private void encodeInt(int i, int tag, int intsize) {
 242 
 243         //
 244         // integer ::= 0x02 asnlength byte {byte}*
 245         //
 246 
 247         if (intsize > 4) {
 248             throw new IllegalArgumentException("BER encode error: INTEGER too long.");
 249         }
 250 
 251         ensureFreeBytes(2 + intsize);
 252 
 253         buf[offset++] = (byte) tag;
 254         buf[offset++] = (byte) intsize;
 255 
 256         int mask = 0xff000000;
 257 
 258         while (intsize-- > 0) {
 259             buf[offset++] = (byte) ((i & mask) >> 24);
 260             i <<= 8;
 261         }
 262     }
 263 
 264     /**
 265      * Encodes a boolean.
 266      *<blockquote><pre>
 267      * BER boolean ::= 0x01 0x01 {0xff|0x00}
 268      *</pre></blockquote>
 269      */
 270     public void encodeBoolean(boolean b) {
 271         encodeBoolean(b, ASN_BOOLEAN);
 272     }
 273 
 274 
 275     /**
 276      * Encodes a boolean and a tag
 277      *<blockquote><pre>
 278      * BER boolean w TAG ::= tag 0x01 {0xff|0x00}
 279      *</pre></blockquote>
 280      */
 281     public void encodeBoolean(boolean b, int tag) {
 282         ensureFreeBytes(3);
 283 
 284         buf[offset++] = (byte) tag;
 285         buf[offset++] = 0x01;
 286         buf[offset++] = b ? (byte) 0xff : (byte) 0x00;
 287     }
 288 
 289     /**
 290      * Encodes a string.
 291      *<blockquote><pre>
 292      * BER string ::= 0x04 strlen byte1 byte2...
 293      *</pre></blockquote>
 294      * The string is converted into bytes using UTF-8 or ISO-Latin-1.
 295      */
 296     public void encodeString(String str, boolean encodeUTF8)
 297         throws EncodeException {
 298         encodeString(str, ASN_OCTET_STR, encodeUTF8);
 299     }
 300 
 301     /**
 302      * Encodes a string and a tag.
 303      *<blockquote><pre>
 304      * BER string w TAG ::= tag strlen byte1 byte2...
 305      *</pre></blockquote>
 306      */
 307     public void encodeString(String str, int tag, boolean encodeUTF8)
 308         throws EncodeException {
 309 
 310         encodeByte(tag);
 311 
 312         int i = 0;
 313         int count;
 314         byte[] bytes = null;
 315 
 316         if (str == null) {
 317             count = 0;
 318         } else if (encodeUTF8) {
 319             try {
 320                 bytes = str.getBytes("UTF8");
 321                 count = bytes.length;
 322             } catch (UnsupportedEncodingException e) {
 323                 throw new EncodeException("UTF8 not available on platform");
 324             }
 325         } else {
 326             try {
 327                 bytes = str.getBytes("8859_1");
 328                 count = bytes.length;
 329             } catch (UnsupportedEncodingException e) {
 330                 throw new EncodeException("8859_1 not available on platform");
 331             }
 332         }
 333 
 334         encodeLength(count);
 335 
 336         ensureFreeBytes(count);
 337         while (i < count) {
 338             buf[offset++] = bytes[i++];
 339         }
 340     }
 341 
 342     /**
 343      * Encodes a portion of an octet string and a tag.
 344      */
 345     public void encodeOctetString(byte tb[], int tag, int tboffset, int length)
 346         throws EncodeException {
 347 
 348         encodeByte(tag);
 349         encodeLength(length);
 350 
 351         if (length > 0) {
 352             ensureFreeBytes(length);
 353             System.arraycopy(tb, tboffset, buf, offset, length);
 354             offset += length;
 355         }
 356     }
 357 
 358     /**
 359       * Encodes an octet string and a tag.
 360       */
 361     public void encodeOctetString(byte tb[], int tag) throws EncodeException {
 362         encodeOctetString(tb, tag, 0, tb.length);
 363     }
 364 
 365     private void encodeLength(int len) throws EncodeException {
 366         ensureFreeBytes(4);     // worst case
 367 
 368         if (len < 128) {
 369             buf[offset++] = (byte) len;
 370         } else if (len <= 0xff) {
 371             buf[offset++] = (byte) 0x81;
 372             buf[offset++] = (byte) len;
 373         } else if (len <= 0xffff) {
 374             buf[offset++] = (byte) 0x82;
 375             buf[offset++] = (byte) (len >> 8);
 376             buf[offset++] = (byte) (len & 0xff);
 377         } else if (len <= 0xffffff) {
 378             buf[offset++] = (byte) 0x83;
 379             buf[offset++] = (byte) (len >> 16);
 380             buf[offset++] = (byte) (len >> 8);
 381             buf[offset++] = (byte) (len & 0xff);
 382         } else {
 383             throw new EncodeException("string too long");
 384         }
 385     }
 386 
 387     /**
 388      * Encodes an array of strings.
 389      */
 390     public void encodeStringArray(String strs[], boolean encodeUTF8)
 391         throws EncodeException {
 392         if (strs == null)
 393             return;
 394         for (int i = 0; i < strs.length; i++) {
 395             encodeString(strs[i], encodeUTF8);
 396         }
 397     }
 398 /*
 399     private void encodeNull() {
 400 
 401         //
 402         // NULL ::= 0x05 0x00
 403         //
 404         encodeByte(0x05);
 405         encodeByte(0x00);
 406     }
 407 */
 408 
 409     /**
 410      * Ensures that there are at least "len" unused bytes in "buf".
 411      * When more space is needed "buf" is expanded by a factor of
 412      * BUF_GROWTH_FACTOR, then "len" bytes are added if "buf" still
 413      * isn't large enough.
 414      */
 415     private void ensureFreeBytes(int len) {
 416         if (bufsize - offset < len) {
 417             int newsize = bufsize * BUF_GROWTH_FACTOR;
 418             if (newsize - offset < len) {
 419                 newsize += len;
 420             }
 421             byte newbuf[] = new byte[newsize];
 422             // Only copy bytes in the range [0, offset)
 423             System.arraycopy(buf, 0, newbuf, 0, offset);
 424 
 425             buf = newbuf;
 426             bufsize = newsize;
 427         }
 428     }
 429 }