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 decoder. Contains methods to parse a BER buffer.
32    *
33    * @author Jagane Sundar
34    * @author Vincent Ryan
35    */
36  public final class BerDecoder extends Ber {
37  
38      private int origOffset;  // The start point in buf to decode
39  
40      /**
41       * Creates a BER decoder that reads bytes from the specified buffer.
42       */
43      public BerDecoder(byte buf[], int offset, int bufsize) {
44  
45          this.buf = buf;         // shared buffer, be careful to use this class
46          this.bufsize = bufsize;
47          this.origOffset = offset;
48  
49          reset();
50      }
51  
52      /**
53       * Resets this decode to start parsing from the initial offset
54       * (ie., same state as after calling the constructor).
55       */
56      public void reset() {
57          offset = origOffset;
58      }
59  
60      /**
61        * Returns the current parse position.
62        * It points to the byte that will be parsed next.
63        * Useful for parsing sequences.
64        */
65      public int getParsePosition() {
66          return offset;
67      }
68  
69      /**
70        * Parses a possibly variable length field.
71        */
72      public int parseLength() throws DecodeException {
73  
74          int lengthbyte = parseByte();
75  
76          if ((lengthbyte & 0x80) == 0x80) {
77  
78              lengthbyte &= 0x7f;
79  
80              if (lengthbyte == 0) {
81                  throw new DecodeException(
82                      "Indefinite length not supported");
83              }
84  
85              if (lengthbyte > 4) {
86                  throw new DecodeException("encoding too long");
87              }
88  
89              if (bufsize - offset < lengthbyte) {
90                  throw new DecodeException("Insufficient data");
91              }
92  
93              int retval = 0;
94  
95              for( int i = 0; i < lengthbyte; i++) {
96                  retval = (retval << 8) + (buf[offset++] & 0xff);
97              }
98              return retval;
99          } else {
100             return lengthbyte;
101         }
102     }
103 
104     /**
105      * Parses the next sequence in this BER buffer.
106      * @param rlen An array for returning size of the sequence in bytes. If null,
107      *          the size is not returned.
108      * @return The sequence's tag.
109      */
110     public int parseSeq(int rlen[]) throws DecodeException {
111 
112         int seq = parseByte();
113         int len = parseLength();
114         if (rlen != null) {
115             rlen[0] = len;
116         }
117         return seq;
118     }
119 
120     /**
121      * Used to skip bytes. Usually used when trying to recover from parse error.
122      * Don't need to be public right now?
123      * @param i The number of bytes to skip
124      */
125     void seek(int i) throws DecodeException {
126         if (offset + i > bufsize || offset + i < 0) {
127             throw new DecodeException("array index out of bounds");
128         }
129         offset += i;
130     }
131 
132     /**
133      * Parses the next byte in this BER buffer.
134      * @return The byte parsed.
135      */
136     public int parseByte() throws DecodeException {
137         if (bufsize - offset < 1) {
138             throw new DecodeException("Insufficient data");
139         }
140         return buf[offset++] & 0xff;
141     }
142 
143 
144     /**
145      * Returns the next byte in this BER buffer without consuming it.
146      * @return The next byte.
147      */
148     public int peekByte() throws DecodeException {
149         if (bufsize - offset < 1) {
150             throw new DecodeException("Insufficient data");
151         }
152         return buf[offset] & 0xff;
153     }
154 
155     /**
156      * Parses an ASN_BOOLEAN tagged integer from this BER buffer.
157      * @return true if the tagged integer is 0; false otherwise.
158      */
159     public boolean parseBoolean() throws DecodeException {
160         return ((parseIntWithTag(ASN_BOOLEAN) == 0x00) ? false : true);
161     }
162 
163     /**
164      * Parses an ASN_ENUMERATED tagged integer from this BER buffer.
165      * @return The tag of enumeration.
166      */
167     public int parseEnumeration() throws DecodeException {
168         return parseIntWithTag(ASN_ENUMERATED);
169     }
170 
171     /**
172      * Parses an ASN_INTEGER tagged integer from this BER buffer.
173      * @return The value of the integer.
174      */
175     public int parseInt() throws DecodeException {
176         return parseIntWithTag(ASN_INTEGER);
177     }
178 
179     /**
180       * Parses an integer that's preceded by a tag.
181       *<blockquote><pre>
182       * BER integer ::= tag length byte {byte}*
183       *</pre></blockquote>
184       */
185     private int parseIntWithTag(int tag) throws DecodeException {
186 
187 
188         if (parseByte() != tag) {
189             throw new DecodeException("Encountered ASN.1 tag " +
190                 Integer.toString(buf[offset - 1] & 0xff) +
191                 " (expected tag " + Integer.toString(tag) + ")");
192         }
193 
194         int len = parseLength();
195 
196         if (len > 4) {
197             throw new DecodeException("INTEGER too long");
198         } else if (len > bufsize - offset) {
199             throw new DecodeException("Insufficient data");
200         }
201 
202         byte fb = buf[offset++];
203         int value = 0;
204 
205         value = fb & 0x7F;
206         for( int i = 1 /* first byte already read */ ; i < len; i++) {
207             value <<= 8;
208             value |= (buf[offset++] & 0xff);
209         }
210 
211         if ((fb & 0x80) == 0x80) {
212             value = -value;
213         }
214 
215         return value;
216     }
217 
218     /**
219       * Parses a string.
220       */
221     public String parseString(boolean decodeUTF8) throws DecodeException {
222         return parseStringWithTag(ASN_SIMPLE_STRING, decodeUTF8, null);
223     }
224 
225     /**
226       * Parses a string of a given tag from this BER buffer.
227       *<blockquote><pre>
228       *BER simple string ::= tag length {byte}*
229       *</pre></blockquote>
230       * @param rlen An array for holding the relative parsed offset; if null
231       *  offset not set.
232       * @param decodeUTF8 If true, use UTF-8 when decoding the string; otherwise
233       * use ISO-Latin-1 (8859_1). Use true for LDAPv3; false for LDAPv2.
234       * @param tag The tag that precedes the string.
235       * @return The non-null parsed string.
236       */
237     public String parseStringWithTag(int tag, boolean decodeUTF8, int rlen[])
238         throws DecodeException {
239 
240         int st;
241         int origOffset = offset;
242 
243         if ((st = parseByte()) != tag) {
244             throw new DecodeException("Encountered ASN.1 tag " +
245                 Integer.toString((byte)st) + " (expected tag " + tag + ")");
246         }
247 
248         int len = parseLength();
249 
250         if (len > bufsize - offset) {
251             throw new DecodeException("Insufficient data");
252         }
253 
254         String retstr;
255         if (len == 0) {
256             retstr = "";
257         } else {
258             byte[] buf2 = new byte[len];
259 
260             System.arraycopy(buf, offset, buf2, 0, len);
261             if (decodeUTF8) {
262                 try {
263                     retstr = new String(buf2, "UTF8");
264                 } catch (UnsupportedEncodingException e) {
265                     throw new DecodeException("UTF8 not available on platform");
266                 }
267             } else {
268                 try {
269                     retstr = new String(buf2, "8859_1");
270                 } catch (UnsupportedEncodingException e) {
271                     throw new DecodeException("8859_1 not available on platform");
272                 }
273             }
274             offset += len;
275         }
276 
277         if (rlen != null) {
278             rlen[0] = offset - origOffset;
279         }
280 
281         return retstr;
282     }
283 
284     /**
285      * Parses an octet string of a given type(tag) from this BER buffer.
286      * <blockquote><pre>
287      * BER Binary Data of type "tag" ::= tag length {byte}*
288      *</pre></blockquote>
289      *
290      * @param tag The tag to look for.
291      * @param rlen An array for returning the relative parsed position. If null,
292      *          the relative parsed position is not returned.
293      * @return A non-null array containing the octet string.
294      * @throws DecodeException If the next byte in the BER buffer is not
295      * <tt>tag</tt>, or if length specified in the BER buffer exceeds the
296      * number of bytes left in the buffer.
297      */
298     public byte[] parseOctetString(int tag, int rlen[]) throws DecodeException {
299 
300         int origOffset = offset;
301         int st;
302         if ((st = parseByte()) != tag) {
303 
304             throw new DecodeException("Encountered ASN.1 tag " +
305                 Integer.toString(st) +
306                 " (expected tag " + Integer.toString(tag) + ")");
307         }
308 
309         int len = parseLength();
310 
311         if (len > bufsize - offset) {
312             throw new DecodeException("Insufficient data");
313         }
314 
315         byte retarr[] = new byte[len];
316         if (len > 0) {
317             System.arraycopy(buf, offset, retarr, 0, len);
318             offset += len;
319         }
320 
321         if (rlen != null) {
322             rlen[0] = offset - origOffset;
323         }
324 
325         return retarr;
326     }
327 
328     /**
329      * Returns the number of unparsed bytes in this BER buffer.
330      */
331     public int bytesLeft() {
332         return bufsize - offset;
333     }
334 }