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             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 }