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
26package com.sun.jndi.ldap;
27
28import 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  */
36public 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            if (retval < 0) {
99              throw new DecodeException("Invalid length bytes");
100            }
101            return retval;
102        } else {
103            return lengthbyte;
104        }
105    }
106
107    /**
108     * Parses the next sequence in this BER buffer.
109     * @param rlen An array for returning size of the sequence in bytes. If null,
110     *          the size is not returned.
111     * @return The sequence's tag.
112     */
113    public int parseSeq(int rlen[]) throws DecodeException {
114
115        int seq = parseByte();
116        int len = parseLength();
117        if (rlen != null) {
118            rlen[0] = len;
119        }
120        return seq;
121    }
122
123    /**
124     * Used to skip bytes. Usually used when trying to recover from parse error.
125     * Don't need to be public right now?
126     * @param i The number of bytes to skip
127     */
128    void seek(int i) throws DecodeException {
129        if (offset + i > bufsize || offset + i < 0) {
130            throw new DecodeException("array index out of bounds");
131        }
132        offset += i;
133    }
134
135    /**
136     * Parses the next byte in this BER buffer.
137     * @return The byte parsed.
138     */
139    public int parseByte() throws DecodeException {
140        if (bufsize - offset < 1) {
141            throw new DecodeException("Insufficient data");
142        }
143        return buf[offset++] & 0xff;
144    }
145
146
147    /**
148     * Returns the next byte in this BER buffer without consuming it.
149     * @return The next byte.
150     */
151    public int peekByte() throws DecodeException {
152        if (bufsize - offset < 1) {
153            throw new DecodeException("Insufficient data");
154        }
155        return buf[offset] & 0xff;
156    }
157
158    /**
159     * Parses an ASN_BOOLEAN tagged integer from this BER buffer.
160     * @return true if the tagged integer is 0; false otherwise.
161     */
162    public boolean parseBoolean() throws DecodeException {
163        return ((parseIntWithTag(ASN_BOOLEAN) == 0x00) ? false : true);
164    }
165
166    /**
167     * Parses an ASN_ENUMERATED tagged integer from this BER buffer.
168     * @return The tag of enumeration.
169     */
170    public int parseEnumeration() throws DecodeException {
171        return parseIntWithTag(ASN_ENUMERATED);
172    }
173
174    /**
175     * Parses an ASN_INTEGER tagged integer from this BER buffer.
176     * @return The value of the integer.
177     */
178    public int parseInt() throws DecodeException {
179        return parseIntWithTag(ASN_INTEGER);
180    }
181
182    /**
183      * Parses an integer that's preceded by a tag.
184      *<blockquote><pre>
185      * BER integer ::= tag length byte {byte}*
186      *</pre></blockquote>
187      */
188    private int parseIntWithTag(int tag) throws DecodeException {
189
190
191        if (parseByte() != tag) {
192            throw new DecodeException("Encountered ASN.1 tag " +
193                Integer.toString(buf[offset - 1] & 0xff) +
194                " (expected tag " + Integer.toString(tag) + ")");
195        }
196
197        int len = parseLength();
198
199        if (len > 4) {
200            throw new DecodeException("INTEGER too long");
201        } else if (len > bufsize - offset) {
202            throw new DecodeException("Insufficient data");
203        }
204
205        byte fb = buf[offset++];
206        int value = 0;
207
208        value = fb & 0x7F;
209        for( int i = 1 /* first byte already read */ ; i < len; i++) {
210            value <<= 8;
211            value |= (buf[offset++] & 0xff);
212        }
213
214        if ((fb & 0x80) == 0x80) {
215            value = -value;
216        }
217
218        return value;
219    }
220
221    /**
222      * Parses a string.
223      */
224    public String parseString(boolean decodeUTF8) throws DecodeException {
225        return parseStringWithTag(ASN_SIMPLE_STRING, decodeUTF8, null);
226    }
227
228    /**
229      * Parses a string of a given tag from this BER buffer.
230      *<blockquote><pre>
231      *BER simple string ::= tag length {byte}*
232      *</pre></blockquote>
233      * @param rlen An array for holding the relative parsed offset; if null
234      *  offset not set.
235      * @param decodeUTF8 If true, use UTF-8 when decoding the string; otherwise
236      * use ISO-Latin-1 (8859_1). Use true for LDAPv3; false for LDAPv2.
237      * @param tag The tag that precedes the string.
238      * @return The non-null parsed string.
239      */
240    public String parseStringWithTag(int tag, boolean decodeUTF8, int rlen[])
241        throws DecodeException {
242
243        int st;
244        int origOffset = offset;
245
246        if ((st = parseByte()) != tag) {
247            throw new DecodeException("Encountered ASN.1 tag " +
248                Integer.toString((byte)st) + " (expected tag " + tag + ")");
249        }
250
251        int len = parseLength();
252
253        if (len > bufsize - offset) {
254            throw new DecodeException("Insufficient data");
255        }
256
257        String retstr;
258        if (len == 0) {
259            retstr = "";
260        } else {
261            byte[] buf2 = new byte[len];
262
263            System.arraycopy(buf, offset, buf2, 0, len);
264            if (decodeUTF8) {
265                try {
266                    retstr = new String(buf2, "UTF8");
267                } catch (UnsupportedEncodingException e) {
268                    throw new DecodeException("UTF8 not available on platform");
269                }
270            } else {
271                try {
272                    retstr = new String(buf2, "8859_1");
273                } catch (UnsupportedEncodingException e) {
274                    throw new DecodeException("8859_1 not available on platform");
275                }
276            }
277            offset += len;
278        }
279
280        if (rlen != null) {
281            rlen[0] = offset - origOffset;
282        }
283
284        return retstr;
285    }
286
287    /**
288     * Parses an octet string of a given type(tag) from this BER buffer.
289     * <blockquote><pre>
290     * BER Binary Data of type "tag" ::= tag length {byte}*
291     *</pre></blockquote>
292     *
293     * @param tag The tag to look for.
294     * @param rlen An array for returning the relative parsed position. If null,
295     *          the relative parsed position is not returned.
296     * @return A non-null array containing the octet string.
297     * @throws DecodeException If the next byte in the BER buffer is not
298     * {@code tag}, or if length specified in the BER buffer exceeds the
299     * number of bytes left in the buffer.
300     */
301    public byte[] parseOctetString(int tag, int rlen[]) throws DecodeException {
302
303        int origOffset = offset;
304        int st;
305        if ((st = parseByte()) != tag) {
306
307            throw new DecodeException("Encountered ASN.1 tag " +
308                Integer.toString(st) +
309                " (expected tag " + Integer.toString(tag) + ")");
310        }
311
312        int len = parseLength();
313
314        if (len > bufsize - offset) {
315            throw new DecodeException("Insufficient data");
316        }
317
318        byte retarr[] = new byte[len];
319        if (len > 0) {
320            System.arraycopy(buf, offset, retarr, 0, len);
321            offset += len;
322        }
323
324        if (rlen != null) {
325            rlen[0] = offset - origOffset;
326        }
327
328        return retarr;
329    }
330
331    /**
332     * Returns the number of unparsed bytes in this BER buffer.
333     */
334    public int bytesLeft() {
335        return bufsize - offset;
336    }
337}
338