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