001 /*
002 * Copyright 2009-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2009-2014 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.asn1;
022
023
024
025 import java.io.BufferedInputStream;
026 import java.io.ByteArrayInputStream;
027 import java.io.InputStream;
028 import java.io.IOException;
029 import java.net.SocketTimeoutException;
030 import java.util.logging.Level;
031 import javax.security.sasl.SaslClient;
032
033 import com.unboundid.util.Mutable;
034 import com.unboundid.util.ThreadSafety;
035 import com.unboundid.util.ThreadSafetyLevel;
036
037 import static com.unboundid.asn1.ASN1Messages.*;
038 import static com.unboundid.util.Debug.*;
039 import static com.unboundid.util.StaticUtils.*;
040
041
042
043 /**
044 * This class provides a mechanism for ASN.1 elements (including sequences and
045 * sets) from an input stream in a manner that allows the data to be decoded on
046 * the fly without constructing {@link ASN1Element} objects if they are not
047 * needed. If any method in this class throws an {@code IOException}, then the
048 * caller must close this reader and must not attempt to use it any more.
049 * {@code ASN1StreamReader} instances are not threadsafe and must not be
050 * accessed concurrently by multiple threads.
051 */
052 @Mutable()
053 @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
054 public final class ASN1StreamReader
055 {
056 // Indicates whether socket timeout exceptions should be ignored for the
057 // initial read of an element.
058 private boolean ignoreInitialSocketTimeout;
059
060 // Indicates whether socket timeout exceptions should be ignored for
061 // subsequent reads of an element.
062 private boolean ignoreSubsequentSocketTimeout;
063
064 // The input stream that will be used for reading data after it has been
065 // unwrapped by SASL processing.
066 private volatile ByteArrayInputStream saslInputStream;
067
068 // The input stream from which data will be read.
069 private final InputStream inputStream;
070
071 // The maximum element size that will be allowed.
072 private final int maxElementSize;
073
074 // The total number of bytes read from the underlying input stream.
075 private long totalBytesRead;
076
077 // The SASL client that will be used to unwrap any data read over this
078 // stream reader.
079 private volatile SaslClient saslClient;
080
081
082
083 /**
084 * Creates a new ASN.1 stream reader that will read data from the provided
085 * input stream. It will use a maximum element size of
086 * {@code Integer.MAX_VALUE}.
087 *
088 * @param inputStream The input stream from which data should be read. If
089 * the provided input stream does not support the use of
090 * the {@code mark} and {@code reset} methods, then it
091 * will be wrapped with a {@code BufferedInputStream}.
092 */
093 public ASN1StreamReader(final InputStream inputStream)
094 {
095 this(inputStream, Integer.MAX_VALUE);
096 }
097
098
099
100 /**
101 * Creates a new ASN.1 stream reader that will read data from the provided
102 * input stream. It will use a maximum element size of
103 * {@code Integer.MAX_VALUE}.
104 *
105 * @param inputStream The input stream from which data should be read.
106 * If the provided input stream does not support the
107 * use of the {@code mark} and {@code reset} methods,
108 * then it will be wrapped with a
109 * {@code BufferedInputStream}.
110 * @param maxElementSize The maximum size in bytes of an ASN.1 element that
111 * may be read. A value less than or equal to zero
112 * will be interpreted as {@code Integer.MAX_VALUE}.
113 */
114 public ASN1StreamReader(final InputStream inputStream,
115 final int maxElementSize)
116 {
117 if (inputStream.markSupported())
118 {
119 this.inputStream = inputStream;
120 }
121 else
122 {
123 this.inputStream = new BufferedInputStream(inputStream);
124 }
125
126 if (maxElementSize > 0)
127 {
128 this.maxElementSize = maxElementSize;
129 }
130 else
131 {
132 this.maxElementSize = Integer.MAX_VALUE;
133 }
134
135 totalBytesRead = 0L;
136 ignoreInitialSocketTimeout = false;
137 ignoreSubsequentSocketTimeout = false;
138 saslClient = null;
139 saslInputStream = null;
140 }
141
142
143
144 /**
145 * Closes this ASN.1 stream reader and the underlying input stream. This
146 * reader must not be used after it has been closed.
147 *
148 * @throws IOException If a problem occurs while closing the underlying
149 * input stream.
150 */
151 public void close()
152 throws IOException
153 {
154 inputStream.close();
155 }
156
157
158
159 /**
160 * Retrieves the total number of bytes read so far from the underlying input
161 * stream.
162 *
163 * @return The total number of bytes read so far from the underlying input
164 * stream.
165 */
166 long getTotalBytesRead()
167 {
168 return totalBytesRead;
169 }
170
171
172
173 /**
174 * Indicates whether to ignore {@code java.net.SocketTimeoutException}
175 * exceptions that may be caught during processing.
176 *
177 * @return {@code true} if {@code SocketTimeoutException} exceptions should
178 * be ignored, or {@code false} if they should not be ignored and
179 * should be propagated to the caller.
180 *
181 * @deprecated Use the {@link #ignoreInitialSocketTimeoutException()} and
182 * {@link #ignoreSubsequentSocketTimeoutException()} methods
183 * instead.
184 */
185 @Deprecated()
186 public boolean ignoreSocketTimeoutException()
187 {
188 return ignoreInitialSocketTimeout;
189 }
190
191
192
193 /**
194 * Indicates whether to ignore {@code java.net.SocketTimeoutException}
195 * exceptions that may be caught while trying to read the first byte of an
196 * element.
197 *
198 * @return {@code true} if {@code SocketTimeoutException} exceptions should
199 * be ignored while trying to read the first byte of an element, or
200 * {@code false} if they should not be ignored and should be
201 * propagated to the caller.
202 */
203 public boolean ignoreInitialSocketTimeoutException()
204 {
205 return ignoreInitialSocketTimeout;
206 }
207
208
209
210 /**
211 * Indicates whether to ignore {@code java.net.SocketTimeoutException}
212 * exceptions that may be caught while trying to read subsequent bytes of an
213 * element (after one or more bytes have already been read for that element).
214 *
215 * @return {@code true} if {@code SocketTimeoutException} exceptions should
216 * be ignored while trying to read subsequent bytes of an element, or
217 * {@code false} if they should not be ignored and should be
218 * propagated to the caller.
219 */
220 public boolean ignoreSubsequentSocketTimeoutException()
221 {
222 return ignoreSubsequentSocketTimeout;
223 }
224
225
226
227 /**
228 * Indicates whether to ignore {@code java.net.SocketTimeoutException}
229 * exceptions that may be caught during processing.
230 *
231 * @param ignoreSocketTimeout Indicates whether to ignore
232 * {@code SocketTimeoutException} exceptions that
233 * may be caught during processing.
234 *
235 * @deprecated Use the {@link #setIgnoreSocketTimeout(boolean,boolean)}
236 * method instead.
237 */
238 @Deprecated()
239 public void setIgnoreSocketTimeout(final boolean ignoreSocketTimeout)
240 {
241 ignoreInitialSocketTimeout = ignoreSocketTimeout;
242 ignoreSubsequentSocketTimeout = ignoreSocketTimeout;
243 }
244
245
246
247 /**
248 * Indicates whether to ignore {@code java.net.SocketTimeoutException}
249 * exceptions that may be caught during processing.
250 *
251 * @param ignoreInitialSocketTimeout Indicates whether to ignore
252 * {@code SocketTimeoutException}
253 * exceptions that may be caught while
254 * trying to read the first byte of an
255 * element.
256 * @param ignoreSubsequentSocketTimeout Indicates whether to ignore
257 * {@code SocketTimeoutException}
258 * exceptions that may be caught while
259 * reading beyond the first byte of an
260 * element.
261 */
262 public void setIgnoreSocketTimeout(final boolean ignoreInitialSocketTimeout,
263 final boolean ignoreSubsequentSocketTimeout)
264 {
265 this.ignoreInitialSocketTimeout = ignoreInitialSocketTimeout;
266 this.ignoreSubsequentSocketTimeout = ignoreSubsequentSocketTimeout;
267 }
268
269
270
271 /**
272 * Peeks at the next byte to be read from the input stream without actually
273 * consuming it.
274 *
275 * @return An integer value encapsulating the BER type of the next element in
276 * the input stream, or -1 if the end of the input stream has been
277 * reached and there is no data to be read. If a value of -1 is
278 * returned, then the input stream will not have been closed since
279 * this method is not intended to have any impact on the underlying
280 * input stream.
281 *
282 * @throws IOException If a problem occurs while reading from the input
283 * stream.
284 */
285 public int peek()
286 throws IOException
287 {
288 final InputStream is;
289 if (saslClient == null)
290 {
291 is = inputStream;
292 }
293 else
294 {
295 if (saslInputStream == null)
296 {
297 readAndDecodeSASLData(-1);
298 }
299
300 is = saslInputStream;
301 }
302
303 is.mark(1);
304 final int byteRead = read(true);
305 is.reset();
306
307 return byteRead;
308 }
309
310
311
312 /**
313 * Reads the BER type of the next element from the input stream. This may not
314 * be called if a previous element has been started but not yet completed.
315 *
316 * @return An integer value encapsulating the BER type of the next element in
317 * the input stream, or -1 if the end of the input stream has been
318 * reached and there is no data to be read. If a value of -1 is
319 * returned, then the input stream will have been closed.
320 *
321 * @throws IOException If a problem occurs while reading from the input
322 * stream.
323 */
324 private int readType()
325 throws IOException
326 {
327 final int typeInt = read(true);
328 if (typeInt < 0)
329 {
330 close();
331 }
332 else
333 {
334 totalBytesRead++;
335 }
336 return typeInt;
337 }
338
339
340
341 /**
342 * Reads the length of the next element from the input stream. This may only
343 * be called after reading the BER type.
344 *
345 * @return The length of the next element from the input stream.
346 *
347 * @throws IOException If a problem occurs while reading from the input
348 * stream, if the end of the stream has been reached, or
349 * if the decoded length is greater than the maximum
350 * allowed length.
351 */
352 private int readLength()
353 throws IOException
354 {
355 int length = read(false);
356 if (length < 0)
357 {
358 throw new IOException(ERR_READ_END_BEFORE_FIRST_LENGTH.get());
359 }
360
361 totalBytesRead++;
362 if (length > 127)
363 {
364 final int numLengthBytes = length & 0x7F;
365 length = 0;
366 if ((numLengthBytes < 1) || (numLengthBytes > 4))
367 {
368 throw new IOException(ERR_READ_LENGTH_TOO_LONG.get(numLengthBytes));
369 }
370
371 for (int i=0; i < numLengthBytes; i++)
372 {
373 final int lengthInt = read(false);
374 if (lengthInt < 0)
375 {
376 throw new IOException(ERR_READ_END_BEFORE_LENGTH_END.get());
377 }
378
379 length <<= 8;
380 length |= (lengthInt & 0xFF);
381 }
382
383 totalBytesRead += numLengthBytes;
384 }
385
386 if ((length < 0) || ((maxElementSize > 0) && (length > maxElementSize)))
387 {
388 throw new IOException(ERR_READ_LENGTH_EXCEEDS_MAX.get(length,
389 maxElementSize));
390 }
391
392 return length;
393 }
394
395
396
397 /**
398 * Skips over the specified number of bytes.
399 *
400 * @param numBytes The number of bytes to skip.
401 *
402 * @throws IOException If a problem occurs while reading from the input
403 * stream, or if the end of the stream is reached before
404 * having skipped the specified number of bytes.
405 */
406 private void skip(final int numBytes)
407 throws IOException
408 {
409 if (numBytes <= 0)
410 {
411 return;
412 }
413
414 if (saslClient != null)
415 {
416 int skippedSoFar = 0;
417 final byte[] skipBuffer = new byte[numBytes];
418 while (true)
419 {
420 final int bytesRead = read(skipBuffer, skippedSoFar,
421 (numBytes - skippedSoFar));
422 if (bytesRead < 0)
423 {
424 // We unexpectedly hit the end of the stream. We'll just return since
425 // we clearly can't skip any more, and subsequent read attempts will
426 // fail.
427 return;
428 }
429
430 skippedSoFar += bytesRead;
431 totalBytesRead += bytesRead;
432 if (skippedSoFar >= numBytes)
433 {
434 return;
435 }
436 }
437 }
438
439 long totalBytesSkipped = inputStream.skip(numBytes);
440 while (totalBytesSkipped < numBytes)
441 {
442 final long bytesSkipped = inputStream.skip(numBytes - totalBytesSkipped);
443 if (bytesSkipped <= 0)
444 {
445 while (totalBytesSkipped < numBytes)
446 {
447 final int byteRead = read(false);
448 if (byteRead < 0)
449 {
450 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
451 }
452 totalBytesSkipped++;
453 }
454 }
455 else
456 {
457 totalBytesSkipped += bytesSkipped;
458 }
459 }
460
461 totalBytesRead += numBytes;
462 }
463
464
465
466 /**
467 * Reads a complete ASN.1 element from the input stream.
468 *
469 * @return The ASN.1 element read from the input stream, or {@code null} if
470 * the end of the input stream was reached before any data could be
471 * read. If {@code null} is returned, then the input stream will
472 * have been closed.
473 *
474 * @throws IOException If a problem occurs while reading from the input
475 * stream, if the end of the input stream is reached in
476 * the middle of the element, or or if an attempt is
477 * made to read an element larger than the maximum
478 * allowed size.
479 */
480 public ASN1Element readElement()
481 throws IOException
482 {
483 final int type = readType();
484 if (type < 0)
485 {
486 return null;
487 }
488
489 final int length = readLength();
490
491 int valueBytesRead = 0;
492 int bytesRemaining = length;
493 final byte[] value = new byte[length];
494 while (valueBytesRead < length)
495 {
496 final int bytesRead = read(value, valueBytesRead, bytesRemaining);
497 if (bytesRead < 0)
498 {
499 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
500 }
501
502 valueBytesRead += bytesRead;
503 bytesRemaining -= bytesRead;
504 }
505
506 totalBytesRead += length;
507 final ASN1Element e = new ASN1Element((byte) type, value);
508 debugASN1Read(e);
509 return e;
510 }
511
512
513
514 /**
515 * Reads an ASN.1 Boolean element from the input stream and returns the value
516 * as a {@code Boolean}.
517 *
518 * @return The {@code Boolean} value of the ASN.1 Boolean element read, or
519 * {@code null} if the end of the input stream was reached before any
520 * data could be read. If {@code null} is returned, then the input
521 * stream will have been closed.
522 *
523 * @throws IOException If a problem occurs while reading from the input
524 * stream, if the end of the input stream is reached in
525 * the middle of the element, or or if an attempt is
526 * made to read an element larger than the maximum
527 * allowed size.
528 *
529 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1
530 * Boolean element.
531 */
532 public Boolean readBoolean()
533 throws IOException, ASN1Exception
534 {
535 final int type = readType();
536 if (type < 0)
537 {
538 return null;
539 }
540
541 final int length = readLength();
542
543 if (length == 1)
544 {
545 final int value = read(false);
546 if (value < 0)
547 {
548 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
549 }
550
551 totalBytesRead++;
552 return (value != 0);
553 }
554 else
555 {
556 skip(length);
557 throw new ASN1Exception(ERR_BOOLEAN_INVALID_LENGTH.get());
558 }
559 }
560
561
562
563 /**
564 * Reads an ASN.1 enumerated element from the input stream and returns the
565 * value as an {@code Integer}.
566 *
567 * @return The {@code Integer} value of the ASN.1 enumerated element read, or
568 * {@code null} if the end of the input stream was reached before any
569 * data could be read. If {@code null} is returned, then the input
570 * stream will have been closed.
571 *
572 * @throws IOException If a problem occurs while reading from the input
573 * stream, if the end of the input stream is reached in
574 * the middle of the element, or or if an attempt is
575 * made to read an element larger than the maximum
576 * allowed size.
577 *
578 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1
579 * enumerated element.
580 */
581 public Integer readEnumerated()
582 throws IOException, ASN1Exception
583 {
584 return readInteger();
585 }
586
587
588
589 /**
590 * Reads an ASN.1 integer element from the input stream and returns the value
591 * as an {@code Integer}.
592 *
593 * @return The {@code Integer} value of the ASN.1 integer element read, or
594 * {@code null} if the end of the input stream was reached before any
595 * data could be read. If {@code null} is returned, then the input
596 * stream will have been closed.
597 *
598 * @throws IOException If a problem occurs while reading from the input
599 * stream, if the end of the input stream is reached in
600 * the middle of the element, or or if an attempt is
601 * made to read an element larger than the maximum
602 * allowed size.
603 *
604 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1
605 * integer element.
606 */
607 public Integer readInteger()
608 throws IOException, ASN1Exception
609 {
610 final int type = readType();
611 if (type < 0)
612 {
613 return null;
614 }
615
616 final int length = readLength();
617 if ((length == 0) || (length > 4))
618 {
619 skip(length);
620 throw new ASN1Exception(ERR_INTEGER_INVALID_LENGTH.get(length));
621 }
622
623 boolean negative = false;
624 int intValue = 0;
625 for (int i=0; i < length; i++)
626 {
627 final int byteRead = read(false);
628 if (byteRead < 0)
629 {
630 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
631 }
632
633 if (i == 0)
634 {
635 negative = ((byteRead & 0x80) != 0x00);
636 }
637
638 intValue <<= 8;
639 intValue |= (byteRead & 0xFF);
640 }
641
642 if (negative)
643 {
644 switch (length)
645 {
646 case 1:
647 intValue |= 0xFFFFFF00;
648 break;
649 case 2:
650 intValue |= 0xFFFF0000;
651 break;
652 case 3:
653 intValue |= 0xFF000000;
654 break;
655 }
656 }
657
658 totalBytesRead += length;
659 return intValue;
660 }
661
662
663
664 /**
665 * Reads an ASN.1 integer element from the input stream and returns the value
666 * as a {@code Long}.
667 *
668 * @return The {@code Long} value of the ASN.1 integer element read, or
669 * {@code null} if the end of the input stream was reached before any
670 * data could be read. If {@code null} is returned, then the input
671 * stream will have been closed.
672 *
673 * @throws IOException If a problem occurs while reading from the input
674 * stream, if the end of the input stream is reached in
675 * the middle of the element, or or if an attempt is
676 * made to read an element larger than the maximum
677 * allowed size.
678 *
679 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1
680 * integer element.
681 */
682 public Long readLong()
683 throws IOException, ASN1Exception
684 {
685 final int type = readType();
686 if (type < 0)
687 {
688 return null;
689 }
690
691 final int length = readLength();
692 if ((length == 0) || (length > 8))
693 {
694 skip(length);
695 throw new ASN1Exception(ERR_LONG_INVALID_LENGTH.get(length));
696 }
697
698 boolean negative = false;
699 long longValue = 0;
700 for (int i=0; i < length; i++)
701 {
702 final int byteRead = read(false);
703 if (byteRead < 0)
704 {
705 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
706 }
707
708 if (i == 0)
709 {
710 negative = ((byteRead & 0x80) != 0x00);
711 }
712
713 longValue <<= 8;
714 longValue |= (byteRead & 0xFFL);
715 }
716
717 if (negative)
718 {
719 switch (length)
720 {
721 case 1:
722 longValue |= 0xFFFFFFFFFFFFFF00L;
723 break;
724 case 2:
725 longValue |= 0xFFFFFFFFFFFF0000L;
726 break;
727 case 3:
728 longValue |= 0xFFFFFFFFFF000000L;
729 break;
730 case 4:
731 longValue |= 0xFFFFFFFF00000000L;
732 break;
733 case 5:
734 longValue |= 0xFFFFFF0000000000L;
735 break;
736 case 6:
737 longValue |= 0xFFFF000000000000L;
738 break;
739 case 7:
740 longValue |= 0xFF00000000000000L;
741 break;
742 }
743 }
744
745 totalBytesRead += length;
746 return longValue;
747 }
748
749
750
751 /**
752 * Reads an ASN.1 null element from the input stream. No value will be
753 * returned but the null element will be consumed.
754 *
755 * @throws IOException If a problem occurs while reading from the input
756 * stream, if the end of the input stream is reached in
757 * the middle of the element, or or if an attempt is
758 * made to read an element larger than the maximum
759 * allowed size.
760 *
761 * @throws ASN1Exception If the data read cannot be parsed as an ASN.1 null
762 * element.
763 */
764 public void readNull()
765 throws IOException, ASN1Exception
766 {
767 final int type = readType();
768 if (type < 0)
769 {
770 return;
771 }
772
773 final int length = readLength();
774
775 if (length != 0)
776 {
777 skip(length);
778 throw new ASN1Exception(ERR_NULL_HAS_VALUE.get());
779 }
780 }
781
782
783
784 /**
785 * Reads an ASN.1 octet string element from the input stream and returns the
786 * value as a byte array.
787 *
788 * @return The byte array value of the ASN.1 octet string element read, or
789 * {@code null} if the end of the input stream was reached before any
790 * data could be read. If {@code null} is returned, then the input
791 * stream will have been closed.
792 *
793 * @throws IOException If a problem occurs while reading from the input
794 * stream, if the end of the input stream is reached in
795 * the middle of the element, or or if an attempt is
796 * made to read an element larger than the maximum
797 * allowed size.
798 */
799 public byte[] readBytes()
800 throws IOException
801 {
802 final int type = readType();
803 if (type < 0)
804 {
805 return null;
806 }
807
808 final int length = readLength();
809
810 int valueBytesRead = 0;
811 int bytesRemaining = length;
812 final byte[] value = new byte[length];
813 while (valueBytesRead < length)
814 {
815 final int bytesRead = read(value, valueBytesRead, bytesRemaining);
816 if (bytesRead < 0)
817 {
818 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
819 }
820
821 valueBytesRead += bytesRead;
822 bytesRemaining -= bytesRead;
823 }
824
825 totalBytesRead += length;
826 return value;
827 }
828
829
830
831 /**
832 * Reads an ASN.1 octet string element from the input stream and returns the
833 * value as a {@code String} using the UTF-8 encoding.
834 *
835 * @return The {@code String} value of the ASN.1 octet string element read,
836 * or {@code null} if the end of the input stream was reached before
837 * any data could be read. If {@code null} is returned, then the
838 * input stream will have been closed.
839 *
840 * @throws IOException If a problem occurs while reading from the input
841 * stream, if the end of the input stream is reached in
842 * the middle of the element, or or if an attempt is
843 * made to read an element larger than the maximum
844 * allowed size.
845 */
846 public String readString()
847 throws IOException
848 {
849 final int type = readType();
850 if (type < 0)
851 {
852 return null;
853 }
854
855 final int length = readLength();
856
857 int valueBytesRead = 0;
858 int bytesRemaining = length;
859 final byte[] value = new byte[length];
860 while (valueBytesRead < length)
861 {
862 final int bytesRead = read(value, valueBytesRead, bytesRemaining);
863 if (bytesRead < 0)
864 {
865 throw new IOException(ERR_READ_END_BEFORE_VALUE_END.get());
866 }
867
868 valueBytesRead += bytesRead;
869 bytesRemaining -= bytesRead;
870 }
871
872 totalBytesRead += length;
873 return toUTF8String(value);
874 }
875
876
877
878 /**
879 * Reads the beginning of an ASN.1 sequence from the input stream and
880 * returns a value that can be used to determine when the end of the sequence
881 * has been reached. Elements which are part of the sequence may be read from
882 * this ASN.1 stream reader until the
883 * {@link ASN1StreamReaderSequence#hasMoreElements} method returns
884 * {@code false}.
885 *
886 * @return An object which may be used to determine when the end of the
887 * sequence has been reached, or {@code null} if the end of the input
888 * stream was reached before any data could be read. If {@code null}
889 * is returned, then the input stream will have been closed.
890 *
891 * @throws IOException If a problem occurs while reading from the input
892 * stream, if the end of the input stream is reached in
893 * the middle of the element, or or if an attempt is
894 * made to read an element larger than the maximum
895 * allowed size.
896 */
897 public ASN1StreamReaderSequence beginSequence()
898 throws IOException
899 {
900 final int type = readType();
901 if (type < 0)
902 {
903 return null;
904 }
905
906 final int length = readLength();
907
908 return new ASN1StreamReaderSequence(this, (byte) type, length);
909 }
910
911
912
913 /**
914 * Reads the beginning of an ASN.1 set from the input stream and returns a
915 * value that can be used to determine when the end of the set has been
916 * reached. Elements which are part of the set may be read from this ASN.1
917 * stream reader until the {@link ASN1StreamReaderSet#hasMoreElements} method
918 * returns {@code false}.
919 *
920 * @return An object which may be used to determine when the end of the set
921 * has been reached, or {@code null} if the end of the input stream
922 * was reached before any data could be read. If {@code null} is
923 * returned, then the input stream will have been closed.
924 *
925 * @throws IOException If a problem occurs while reading from the input
926 * stream, if the end of the input stream is reached in
927 * the middle of the element, or or if an attempt is
928 * made to read an element larger than the maximum
929 * allowed size.
930 */
931 public ASN1StreamReaderSet beginSet()
932 throws IOException
933 {
934 final int type = readType();
935 if (type < 0)
936 {
937 return null;
938 }
939
940 final int length = readLength();
941
942 return new ASN1StreamReaderSet(this, (byte) type, length);
943 }
944
945
946
947 /**
948 * Reads a byte of data from the underlying input stream, optionally ignoring
949 * socket timeout exceptions.
950 *
951 * @param initial Indicates whether this is the initial read for an element.
952 *
953 * @return The byte read from the input stream, or -1 if the end of the
954 * input stream was reached.
955 *
956 * @throws IOException If a problem occurs while reading data.
957 */
958 private int read(final boolean initial)
959 throws IOException
960 {
961 if (saslClient != null)
962 {
963 if (saslInputStream != null)
964 {
965 final int b = saslInputStream.read();
966 if (b >= 0)
967 {
968 return b;
969 }
970 }
971
972 readAndDecodeSASLData(-1);
973 return saslInputStream.read();
974 }
975
976 try
977 {
978 final int b = inputStream.read();
979 if ((saslClient == null) || (b < 0))
980 {
981 return b;
982 }
983 else
984 {
985 // This should only happen the first time after the SASL client has been
986 // installed.
987 readAndDecodeSASLData(b);
988 return saslInputStream.read();
989 }
990 }
991 catch (SocketTimeoutException ste)
992 {
993 debugException(Level.FINEST, ste);
994
995 if ((initial && ignoreInitialSocketTimeout) ||
996 ((! initial) && ignoreSubsequentSocketTimeout))
997 {
998 while (true)
999 {
1000 try
1001 {
1002 return inputStream.read();
1003 }
1004 catch (SocketTimeoutException ste2)
1005 {
1006 debugException(Level.FINEST, ste2);
1007 }
1008 }
1009 }
1010 else
1011 {
1012 throw ste;
1013 }
1014 }
1015 }
1016
1017
1018
1019 /**
1020 * Reads data from the underlying input stream, optionally ignoring socket
1021 * timeout exceptions.
1022 *
1023 * @param buffer The buffer into which the data should be read.
1024 * @param offset The position at which to start placing the data that was
1025 * read.
1026 * @param length The maximum number of bytes to read.
1027 *
1028 * @return The number of bytes read, or -1 if the end of the input stream
1029 * was reached.
1030 *
1031 * @throws IOException If a problem occurs while reading data.
1032 */
1033 private int read(final byte[] buffer, final int offset, final int length)
1034 throws IOException
1035 {
1036 if (saslClient != null)
1037 {
1038 if (saslInputStream != null)
1039 {
1040 final int bytesRead = saslInputStream.read(buffer, offset, length);
1041 if (bytesRead > 0)
1042 {
1043 return bytesRead;
1044 }
1045 }
1046
1047 readAndDecodeSASLData(-1);
1048 return saslInputStream.read(buffer, offset, length);
1049 }
1050
1051 try
1052 {
1053 return inputStream.read(buffer, offset, length);
1054 }
1055 catch (SocketTimeoutException ste)
1056 {
1057 debugException(Level.FINEST, ste);
1058 if (ignoreSubsequentSocketTimeout)
1059 {
1060 while (true)
1061 {
1062 try
1063 {
1064 return inputStream.read(buffer, offset, length);
1065 }
1066 catch (SocketTimeoutException ste2)
1067 {
1068 debugException(Level.FINEST, ste2);
1069 }
1070 }
1071 }
1072 else
1073 {
1074 throw ste;
1075 }
1076 }
1077 }
1078
1079
1080
1081 /**
1082 * Sets the SASL client to use to unwrap any data read over this ASN.1 stream
1083 * reader.
1084 *
1085 * @param saslClient The SASL client to use to unwrap any data read over
1086 * this ASN.1 stream reader.
1087 */
1088 void setSASLClient(final SaslClient saslClient)
1089 {
1090 this.saslClient = saslClient;
1091 }
1092
1093
1094
1095 /**
1096 * Reads data from the underlying input stream, unwraps it using the
1097 * configured SASL client, and makes the result available in a byte array
1098 * input stream that will be used for subsequent reads.
1099 *
1100 * @param firstByte The first byte that has already been read. This should
1101 * only be used if the value is greater than or equal to
1102 * zero.
1103 *
1104 * @throws IOException If a problem is encountered while reading from the
1105 * underlying input stream or decoding the data that
1106 * has been read.
1107 */
1108 private void readAndDecodeSASLData(final int firstByte)
1109 throws IOException
1110 {
1111 // The first four bytes must be the number of bytes of data to unwrap.
1112 int numWrappedBytes = 0;
1113 int numLengthBytes = 4;
1114 if (firstByte >= 0)
1115 {
1116 numLengthBytes = 3;
1117 numWrappedBytes = firstByte;
1118 }
1119
1120 for (int i=0; i < numLengthBytes; i++)
1121 {
1122 final int b = inputStream.read();
1123 if (b < 0)
1124 {
1125 if ((i == 0) && (firstByte < 0))
1126 {
1127 // This means that we hit the end of the input stream without
1128 // reading any data. This is fine and just means that the end of
1129 // the input stream has been reached.
1130 saslInputStream = new ByteArrayInputStream(NO_BYTES);
1131 }
1132 else
1133 {
1134 // This means that we hit the end of the input stream after having
1135 // read a portion of the number of wrapped bytes. This is an error.
1136 throw new IOException(
1137 ERR_STREAM_READER_EOS_READING_SASL_LENGTH.get(i));
1138 }
1139 }
1140 else
1141 {
1142 numWrappedBytes = (numWrappedBytes << 8) | (b & 0xFF);
1143 }
1144 }
1145
1146 if ((maxElementSize > 0) && (numWrappedBytes > maxElementSize))
1147 {
1148 throw new IOException(ERR_READ_SASL_LENGTH_EXCEEDS_MAX.get(
1149 numWrappedBytes, maxElementSize));
1150 }
1151
1152 int wrappedDataPos = 0;
1153 final byte[] wrappedData = new byte[numWrappedBytes];
1154 while (true)
1155 {
1156 final int numBytesRead = inputStream.read(wrappedData, wrappedDataPos,
1157 (numWrappedBytes - wrappedDataPos));
1158 if (numBytesRead < 0)
1159 {
1160 throw new IOException(ERR_STREAM_READER_EOS_READING_SASL_DATA.get(
1161 wrappedDataPos, numWrappedBytes));
1162 }
1163
1164 wrappedDataPos += numBytesRead;
1165 if (wrappedDataPos >= numWrappedBytes)
1166 {
1167 break;
1168 }
1169 }
1170
1171 final byte[] unwrappedData =
1172 saslClient.unwrap(wrappedData, 0, numWrappedBytes);
1173 saslInputStream = new ByteArrayInputStream(unwrappedData, 0,
1174 unwrappedData.length);
1175 }
1176 }