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