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 if (parseByte() != tag) { 190 // Ber could have been reset; 191 String s; 192 if (offset > 0) { 193 s = Integer.toString(buf[offset - 1] & 0xff); 194 } else { 195 s = "Empty tag"; 196 } 197 throw new DecodeException("Encountered ASN.1 tag " + 198 s + " (expected tag " + Integer.toString(tag) + ")"); 199 } 200 201 int len = parseLength(); 202 203 if (len > 4) { 204 throw new DecodeException("INTEGER too long"); 205 } else if (len > bufsize - offset) { 206 throw new DecodeException("Insufficient data"); 207 } 208 209 byte fb = buf[offset++]; 210 int value = 0; 211 212 value = fb & 0x7F; 213 for( int i = 1 /* first byte already read */ ; i < len; i++) { 214 value <<= 8; 215 value |= (buf[offset++] & 0xff); 216 } 217 218 if ((fb & 0x80) == 0x80) { 219 value = -value; 220 } 221 222 return value; 223 } 224 225 /** 226 * Parses a string. 227 */ 228 public String parseString(boolean decodeUTF8) throws DecodeException { 229 return parseStringWithTag(ASN_SIMPLE_STRING, decodeUTF8, null); 230 } 231 232 /** 233 * Parses a string of a given tag from this BER buffer. 234 *<blockquote><pre> 235 *BER simple string ::= tag length {byte}* 236 *</pre></blockquote> 237 * @param rlen An array for holding the relative parsed offset; if null 238 * offset not set. 239 * @param decodeUTF8 If true, use UTF-8 when decoding the string; otherwise 240 * use ISO-Latin-1 (8859_1). Use true for LDAPv3; false for LDAPv2. 241 * @param tag The tag that precedes the string. 242 * @return The non-null parsed string. 243 */ 244 public String parseStringWithTag(int tag, boolean decodeUTF8, int rlen[]) 245 throws DecodeException { 246 247 int st; 248 int origOffset = offset; 249 250 if ((st = parseByte()) != tag) { 251 throw new DecodeException("Encountered ASN.1 tag " + 252 Integer.toString((byte)st) + " (expected tag " + tag + ")"); 253 } 254 255 int len = parseLength(); 256 257 if (len > bufsize - offset) { 258 throw new DecodeException("Insufficient data"); 259 } 260 261 String retstr; 262 if (len == 0) { 263 retstr = ""; 264 } else { 265 byte[] buf2 = new byte[len]; 266 267 System.arraycopy(buf, offset, buf2, 0, len); 268 if (decodeUTF8) { 269 try { 270 retstr = new String(buf2, "UTF8"); 271 } catch (UnsupportedEncodingException e) { 272 throw new DecodeException("UTF8 not available on platform"); 273 } 274 } else { 275 try { 276 retstr = new String(buf2, "8859_1"); 277 } catch (UnsupportedEncodingException e) { 278 throw new DecodeException("8859_1 not available on platform"); 279 } 280 } 281 offset += len; 282 } 283 284 if (rlen != null) { 285 rlen[0] = offset - origOffset; 286 } 287 288 return retstr; 289 } 290 291 /** 292 * Parses an octet string of a given type(tag) from this BER buffer. 293 * <blockquote><pre> 294 * BER Binary Data of type "tag" ::= tag length {byte}* 295 *</pre></blockquote> 296 * 297 * @param tag The tag to look for. 298 * @param rlen An array for returning the relative parsed position. If null, 299 * the relative parsed position is not returned. 300 * @return A non-null array containing the octet string. 301 * @throws DecodeException If the next byte in the BER buffer is not 302 * <tt>tag</tt>, or if length specified in the BER buffer exceeds the 303 * number of bytes left in the buffer. 304 */ 305 public byte[] parseOctetString(int tag, int rlen[]) throws DecodeException { 306 307 int origOffset = offset; 308 int st; 309 if ((st = parseByte()) != tag) { 310 311 throw new DecodeException("Encountered ASN.1 tag " + 312 Integer.toString(st) + 313 " (expected tag " + Integer.toString(tag) + ")"); 314 } 315 316 int len = parseLength(); 317 318 if (len > bufsize - offset) { 319 throw new DecodeException("Insufficient data"); 320 } 321 322 byte retarr[] = new byte[len]; 323 if (len > 0) { 324 System.arraycopy(buf, offset, retarr, 0, len); 325 offset += len; 326 } 327 328 if (rlen != null) { 329 rlen[0] = offset - origOffset; 330 } 331 332 return retarr; 333 } 334 335 /** 336 * Returns the number of unparsed bytes in this BER buffer. 337 */ 338 public int bytesLeft() { 339 return bufsize - offset; 340 } 341 }