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