View Javadoc
1   /*
2    * Copyright (c) 1999, 2013, 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 }