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 }