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 * <tt>tag</tt>, 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 }