001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.protocols.asn1; 028 import org.opends.messages.Message; 029 030 031 032 import java.io.InputStream; 033 import java.io.IOException; 034 import java.net.Socket; 035 036 import static org.opends.server.loggers.debug.DebugLogger.*; 037 import org.opends.server.loggers.debug.DebugTracer; 038 import org.opends.server.types.DebugLogLevel; 039 import static org.opends.messages.ProtocolMessages.*; 040 041 042 043 /** 044 * This class defines a utility that can be used to read ASN.1 elements from a 045 * provided socket or input stream. 046 */ 047 @org.opends.server.types.PublicAPI( 048 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED, 049 mayInstantiate=true, 050 mayExtend=false, 051 mayInvoke=true) 052 public final class ASN1Reader 053 { 054 /** 055 * The tracer object for the debug logger. 056 */ 057 private static final DebugTracer TRACER = getTracer(); 058 059 060 061 062 // The input stream from which to read the ASN.1 elements. 063 private InputStream inputStream; 064 065 // The largest element size (in bytes) that will be allowed. 066 private int maxElementSize; 067 068 // The socket with which the input stream is associated. 069 private Socket socket; 070 071 072 073 /** 074 * Creates a new ASN.1 reader that will read elements from the provided 075 * socket. 076 * 077 * @param socket The socket from which to read the ASN.1 elements. 078 * 079 * @throws IOException If a problem occurs while attempting to obtain the 080 * input stream for the socket. 081 */ 082 public ASN1Reader(Socket socket) 083 throws IOException 084 { 085 this.socket = socket; 086 inputStream = socket.getInputStream(); 087 088 maxElementSize = -1; 089 } 090 091 092 093 /** 094 * Creates a new ASN.1 reader that will read elements from the provided input 095 * stream. 096 * 097 * @param inputStream The input stream from which to read the ASN.1 098 * elements. 099 */ 100 public ASN1Reader(InputStream inputStream) 101 { 102 this.inputStream = inputStream; 103 socket = null; 104 maxElementSize = -1; 105 } 106 107 108 109 /** 110 * Retrieves the maximum size in bytes that will be allowed for elements read 111 * using this reader. A negative value indicates that no limit should be 112 * enforced. 113 * 114 * @return The maximum size in bytes that will be allowed for elements. 115 */ 116 public int getMaxElementSize() 117 { 118 return maxElementSize; 119 } 120 121 122 123 /** 124 * Specifies the maximum size in bytes that will be allowed for elements. A 125 * negative value indicates that no limit should be enforced. 126 * 127 * @param maxElementSize The maximum size in bytes that will be allowed for 128 * elements read using this reader. 129 */ 130 public void setMaxElementSize(int maxElementSize) 131 { 132 this.maxElementSize = maxElementSize; 133 } 134 135 136 137 /** 138 * Retrieves the maximum length of time in milliseconds that this reader will 139 * be allowed to block while waiting to read data. This is only applicable 140 * for readers created with sockets rather than input streams. 141 * 142 * @return The maximum length of time in milliseconds that this reader will 143 * be allowed to block while waiting to read data, or 0 if there is 144 * no limit, or -1 if this ASN.1 reader is not associated with a 145 * socket and no timeout can be enforced. 146 * 147 * @throws IOException If a problem occurs while polling the socket to 148 * determine the timeout. 149 */ 150 public int getIOTimeout() 151 throws IOException 152 { 153 if (socket == null) 154 { 155 return -1; 156 } 157 else 158 { 159 return socket.getSoTimeout(); 160 } 161 } 162 163 164 165 /** 166 * Specifies the maximum length of time in milliseconds that this reader 167 * should be allowed to block while waiting to read data. This will only be 168 * applicable for readers created with sockets and will have no effect on 169 * readers created with input streams. 170 * 171 * @param ioTimeout The maximum length of time in milliseconds that this 172 * reader should be allowed to block while waiting to read 173 * data, or 0 if there should be no limit. 174 * 175 * @throws IOException If a problem occurs while setting the underlying 176 * socket option. 177 */ 178 public void setIOTimeout(int ioTimeout) 179 throws IOException 180 { 181 if (socket == null) 182 { 183 return; 184 } 185 186 socket.setSoTimeout(Math.max(0, ioTimeout)); 187 } 188 189 190 191 /** 192 * Reads an ASN.1 element from the associated input stream. 193 * 194 * @return The ASN.1 element read from the associated input stream, or 195 * <CODE>null</CODE> if the end of the stream has been reached. 196 * 197 * @throws IOException If a problem occurs while attempting to read from the 198 * input stream. 199 * 200 * @throws ASN1Exception If a problem occurs while attempting to decode the 201 * data read as an ASN.1 element. 202 */ 203 public ASN1Element readElement() 204 throws IOException, ASN1Exception 205 { 206 // First, read the BER type, which should be the first byte. 207 int typeValue = inputStream.read(); 208 if (typeValue < 0) 209 { 210 return null; 211 } 212 213 byte type = (byte) (typeValue & 0xFF); 214 215 216 217 // Next, read the first byte of the length, and see if we need to read more. 218 int length = inputStream.read(); 219 int numLengthBytes = (length & 0x7F); 220 if (length != numLengthBytes) 221 { 222 // Make sure that there are an acceptable number of bytes in the length. 223 if (numLengthBytes > 4) 224 { 225 Message message = 226 ERR_ASN1_ELEMENT_SET_INVALID_NUM_LENGTH_BYTES.get(numLengthBytes); 227 throw new ASN1Exception(message); 228 } 229 230 231 length = 0; 232 for (int i=0; i < numLengthBytes; i++) 233 { 234 int lengthByte = inputStream.read(); 235 if (lengthByte < 0) 236 { 237 // We've reached the end of the stream in the middle of the value. 238 // This is not good, so throw an exception. 239 Message message = 240 ERR_ASN1_ELEMENT_SET_TRUNCATED_LENGTH.get(numLengthBytes); 241 throw new IOException(message.toString()); 242 } 243 244 length = (length << 8) | lengthByte; 245 } 246 } 247 248 249 // See how many bytes there are in the value. If there are none, then just 250 // create an empty element with only a type. If the length is larger than 251 // the maximum allowed, then fail. 252 if (length == 0) 253 { 254 return new ASN1Element(type); 255 } 256 else if ((maxElementSize > 0) && (length > maxElementSize)) 257 { 258 Message message = 259 ERR_ASN1_READER_MAX_SIZE_EXCEEDED.get(length, maxElementSize); 260 throw new ASN1Exception(message); 261 } 262 263 264 // There is a value for the element, so create an array to hold it and read 265 // it from the stream. 266 byte[] value = new byte[length]; 267 int readPos = 0; 268 int bytesNeeded = length; 269 while (bytesNeeded > 0) 270 { 271 int bytesRead = inputStream.read(value, readPos, bytesNeeded); 272 if (bytesRead < 0) 273 { 274 Message message = 275 ERR_ASN1_ELEMENT_SET_TRUNCATED_VALUE.get(length, bytesNeeded); 276 throw new IOException(message.toString()); 277 } 278 279 bytesNeeded -= bytesRead; 280 readPos += bytesRead; 281 } 282 283 284 // Return the constructed element. 285 return new ASN1Element(type, value); 286 } 287 288 289 290 /** 291 * Closes this ASN.1 reader and the underlying input stream and/or socket. 292 */ 293 public void close() 294 { 295 try 296 { 297 inputStream.close(); 298 } 299 catch (Exception e) 300 { 301 if (debugEnabled()) 302 { 303 TRACER.debugCaught(DebugLogLevel.ERROR, e); 304 } 305 } 306 307 if (socket != null) 308 { 309 try 310 { 311 socket.close(); 312 } 313 catch (Exception e) 314 { 315 if (debugEnabled()) 316 { 317 TRACER.debugCaught(DebugLogLevel.ERROR, e); 318 } 319 } 320 } 321 } 322 } 323